I believe the question you need to ask yourself is of you really need this
abstraction in the first place. You are now using a new platform but you
are worried about concerns that are more relevant in the platform you
originally came from.

First of all, I don't believe in swapping the implementation in tests as a
general pattern/feature. Every time you swap, you still need to write an
integration test to guarantee the two layers work together, so what are you
gaining? If you are going to swap only in some cases, I would rather define
proper contracts for those particular cases instead of swapping an
implementation detail (I wrote about this in Plataformatec blog, I am on my
phone right now but the digest is that, for an Twitter http client, you
mock the "get_tweets" functionality and not the Http client itself).

Similarly concerns like caching and tests performance are going to hit you
much later in Elixir and you can still provide that at the Ecto repository
level instead of having to write your own user repositories.

It is really common for OO developers to think of Elixir modules as the
minimal code structure (and therefore breaking everything into modules) and
forget about functions. My advice would be to think of those as simply
regular functions that are grouped based on functionality rather than on
structure, for example MyApp.Accounts.get_active_users.

On Sunday, June 12, 2016, Larry Weya <[email protected]> wrote:

> I'm currently re-implementing part of a Laravel(PHP) codebase in Elixir
> where I had used the repository pattern
> <http://blog.gauffin.org/2013/01/repository-pattern-done-right/> in only
> some of the data models. In the Elixir implementation, I'd like to use it
> across all the data models mainly because
>
> 1. I can swap out the implementation within tests
> 2. I can swap out the implementation to e.g. go through a cache layer for
> reads
> 3. I want to able to use different ecto repos for e.g. read and write and
> having abstract repositories would allow this to be done from a central
> place
>
> I worked on an initial implementation this weekend that works like this.
>
> For each data entity, implement a "behaviour" with callbacks for that
> particular entity e.g. a UserRepository where you need to query for a list
> of active users would look like this
>
> defmodule UserRepository do
>   use Repository.Base, # parses opts and config and sets @repository to
> configured repository module
>     otp_app: :my_app
>     repository: MyApp.Repositories.EctoUserRepository # this points to
> the concrete implementation and should be set within the config
>
>   @callback save(Ecto.Changeset.t) :: {:ok, User.t} | {:error,
> Ecto.Changeset.t}
>
>   @callback get_active(non_neg_integer, non_neg_integer) :: [User.t]
> end
>
> The Ecto backed repository implementation would look like this
>
> defmodule EctoUserRepository do
>   use Repository.Ecto.Base, # parses the opts and config, sets @repo and
> @read_repo and implements a save function thats common to all Ecto repos
>     repo: MyApp.Repo,
>     read_repo: MyApp.Repo # could set a different repo for reads
>
>   @behaviour UserRepository
>
>   import Ecto.Query, only: [from: 2]
>
>   def get_active(offset, limit) do
>     (from u in User,
>       where: u.active == true,
>       offset: ^offset,
>       limit: ^limit)
>     |> @read_repo.all()
>   end
> end
>
> Please share any thoughts on this.
>
> Now this is where I need some help, I want to able to use the repository as
>
> UserRepository.get_active(0, 20)
>
> what I've done at the moment is redefine each function within the
> UserRepository and delegate to whatever repository is configured
>
> defmodule UserRepository do
>   ...
>
>   @callback save(Ecto.Changeset.t) :: {:ok, User.t} | {:error,
> Ecto.Changeset.t}
>
>   @callback get_active(non_neg_integer, non_neg_integer) :: [User.t]
>
>     def save(changeset) do
>     @repository.save(changeset)
>   end
>
>   def get_active(offset, limit) do
>     @repository.get_active(offset, limit)
>   end
> end
>
>
> I find that this is quite repetitive and forces the same for all
> repository implementations and functions.
>
>
> --
> You received this message because you are subscribed to the Google Groups
> "elixir-lang-talk" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected]
> <javascript:_e(%7B%7D,'cvml','elixir-lang-talk%[email protected]');>
> .
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/elixir-lang-talk/47df7f07-0e79-40d7-b551-86839dba22b3%40googlegroups.com
> <https://groups.google.com/d/msgid/elixir-lang-talk/47df7f07-0e79-40d7-b551-86839dba22b3%40googlegroups.com?utm_medium=email&utm_source=footer>
> .
> For more options, visit https://groups.google.com/d/optout.
>


-- 


*José Valim*
www.plataformatec.com.br
Skype: jv.ptec
Founder and Director of R&D

-- 
You received this message because you are subscribed to the Google Groups 
"elixir-lang-talk" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/elixir-lang-talk/CAGnRm4%2BxhyaQz_TD7n%3DOumGcccvXGTEqCA%3DaX47akdrQqPRUEQ%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to