DAL v0.3.0 DAL

Global Data Access Layer to replace direct use of Ecto.Repo and other data services.

The DAL performs two core functions:

  1. Provides an abstraction for other data repositories
  2. Provides a set of macros representing all available data types that can be used in services

Data Abstraction

We use a number of different databases at Expert360. Right now all of them are SQL (either MySQL or PostgreSQL) and use Ecto.Repo but in the future they may also be other databases such as Redis or Elasticsearch.

The DAL uses a discovery mechanism (see DAL.Repo.Discovery) which uses Elixir protocols to determine what Repo and attached database to use for a particular query. This way the caller does not need to know where to find the data - they just need to query the DAL.

The DAL emulates the Ecto.Repo API so that it can be used in place of an actual Repo in most scenarios (for example in ex_machina factories). But be aware that it does not actually adhere to the Ecto.Repo behaviour as it does not define callbacks such as start_link/1.

You can use the DAL in exactly the same way that you would a normal ecto repo:

DAL.get(Profiles.Project, 1)

Schema Macros

In the DAL architecture, services need to define their own schema models. However, to have multiple services that define schema fields would be cumbersome and error prone.

Consequently, the DAL defines macros for each data type that it supports (including implementations for DAL.Repo.Discoverable) that can be used by services.

For example, to create a project schema model in the Profiles service:

defmodule Profiles.Project do
  use DAL.Types.Project
end

Link to this section Summary

Functions

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.aggregate/4

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.get_by!/2

Delegates to an appropriate repo determined by DAL.Repo.Discoverable and then constrains the results to the given list of ids before passing to all/2

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.delete/2

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.delete!/2

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.delete_all/2

Convenience function for using repo discovery

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.get/3

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.get!/3

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.get_by/3

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.get_by!/3

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.insert/2

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.insert!/2

Helper function that inserts a list of Ecto.Changeset via Ecto.Multi, wrapping it in a transaction.'

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.insert_or_update/2

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.insert_or_update!/2

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.one!/2

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.stream/2

Wrapper over Ecto.transaction to handle Ecto.Multi and a standard Ecto.Query, built in repo discovery

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.update/2

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.update!/2

Link to this section Functions

Link to this function

aggregate(queryable, aggregate, field, opts \\ [])
aggregate(
  queryable :: Ecto.Query.t(),
  aggregate :: :avg | :count | :max | :min | :sum,
  field :: atom(),
  opts :: Keyword.t()
) :: [Ecto.Schema.t()] | no_return()

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.aggregate/4

Link to this function

all(queryable, opts \\ [])
all(queryable :: Ecto.Query.t(), opts :: Keyword.t()) ::
  [Ecto.Schema.t()] | no_return()

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.get_by!/2

Link to this function

all_ids(queryable, id_list, opts \\ [])
all_ids(queryable :: Ecto.Query.t(), id_list :: [term()], opts :: Keyword.t()) ::
  [Ecto.Schema.t()] | no_return()

Delegates to an appropriate repo determined by DAL.Repo.Discoverable and then constrains the results to the given list of ids before passing to all/2.

Link to this function

delete(struct_or_changeset, opts \\ [])
delete(
  struct_or_changeset :: Ecto.Schema.t() | Ecto.Changeset.t(),
  opts :: Keyword.t()
) :: {:ok, Ecto.Schema.t()} | {:error, Ecto.Changeset.t()}

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.delete/2

Link to this function

delete!(struct_or_changeset, opts \\ [])
delete!(
  struct_or_changeset :: Ecto.Schema.t() | Ecto.Changeset.t(),
  opts :: Keyword.t()
) :: Ecto.Schema.t() | no_return()

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.delete!/2

Link to this function

delete_all(queryable, opts \\ [])
delete_all(queryable :: Ecto.Queryable.t(), opts :: Keyword.t()) ::
  {:ok, Ecto.Schema.t()} | {:error, Ecto.Changeset.t()}

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.delete_all/2

Link to this function

discover(structs)
discover(
  queryable ::
    nil
    | Ecto.Query.t()
    | Ecto.Changeset.t()
    | Ecto.Schema.t()
    | [Ecto.Schema.t()]
) :: Ecto.Repo.t()

Convenience function for using repo discovery.

Link to this function

get(queryable, id, opts \\ [])
get(queryable :: Ecto.Queryable.t(), id :: term(), opts :: Keyword.t()) ::
  Ecto.Schema.t() | nil | no_return()

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.get/3

Link to this function

get!(queryable, id, opts \\ [])
get!(queryable :: Ecto.Queryable.t(), id :: term(), opts :: Keyword.t()) ::
  Ecto.Schema.t() | nil | no_return()

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.get!/3

Link to this function

get_by(queryable, params, opts \\ [])
get_by(
  queryable :: Ecto.Queryable.t(),
  clauses :: Keyword.t() | map(),
  opts :: Keyword.t()
) :: Ecto.Schema.t() | nil | no_return()

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.get_by/3

Link to this function

get_by!(queryable, params, opts \\ [])
get_by!(
  queryable :: Ecto.Queryable.t(),
  clauses :: Keyword.t() | map(),
  opts :: Keyword.t()
) :: Ecto.Schema.t() | nil | no_return()

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.get_by!/3

Link to this function

insert(struct_or_changeset, opts \\ [])
insert(
  struct_or_changeset :: Ecto.Schema.t() | Ecto.Changeset.t(),
  opts :: Keyword.t()
) :: {:ok, Ecto.Schema.t()} | {:error, Ecto.Changeset.t()}

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.insert/2

Link to this function

insert!(struct_or_changeset, opts \\ [])
insert!(
  struct_or_changeset :: Ecto.Schema.t() | Ecto.Changeset.t(),
  opts :: Keyword.t()
) :: Ecto.Schema.t() | no_return()

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.insert!/2

Link to this function

insert_all(schema_or_source, entries, opts \\ [])
insert_all(
  schema_or_source :: binary() | {binary(), Ecto.Schema.t()} | Ecto.Schema.t(),
  entries :: [map() | Keyword.t()],
  opts :: Keyword.t()
) :: {integer(), nil | [term()]} | no_return()

Callback implementation for DAL.Behaviour.insert_all/3.

Link to this function

insert_bulk(changesets, opts \\ [])
insert_bulk(changesets :: [Ecto.Changeset.t()], [{:opts, list()}]) ::
  {:ok, [map()]} | {:error, Ecto.Changeset.t()}

Helper function that inserts a list of Ecto.Changeset via Ecto.Multi, wrapping it in a transaction.'

Link to this function

insert_or_update(struct_or_changeset, opts \\ [])
insert_or_update(
  struct_or_changeset :: Ecto.Schema.t() | Ecto.Changeset.t(),
  opts :: Keyword.t()
) :: {:ok, Ecto.Schema.t()} | {:error, Ecto.Changeset.t()}

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.insert_or_update/2

Link to this function

insert_or_update!(struct_or_changeset, opts \\ [])
insert_or_update!(
  struct_or_changeset :: Ecto.Schema.t() | Ecto.Changeset.t(),
  opts :: Keyword.t()
) :: {:ok, Ecto.Schema.t()} | {:error, Ecto.Changeset.t()}

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.insert_or_update!/2

Link to this function

one(queryable, opts \\ [])
one(queryable :: Ecto.Query.t(), opts :: Keyword.t()) ::
  Ecto.Schema.t() | nil | no_return()

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.one!/2

Link to this function

preload(struct_or_structs_or_nil, preloads, opts \\ [])

Link to this function

stream(queryable, opts \\ [])
stream(queryable :: Ecto.Query.t(), opts :: Keyword.t()) :: Enum.t()

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.stream/2

Link to this function

transaction(queryable, opts \\ [])

Wrapper over Ecto.transaction to handle Ecto.Multi and a standard Ecto.Query, built in repo discovery.

Link to this function

update(struct_or_changeset, opts \\ [])
update(
  struct_or_changeset :: Ecto.Schema.t() | Ecto.Changeset.t(),
  opts :: Keyword.t()
) :: {:ok, Ecto.Schema.t()} | {:error, Ecto.Changeset.t()}

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.update/2

Link to this function

update!(struct_or_changeset, opts \\ [])
update!(
  struct_or_changeset :: Ecto.Schema.t() | Ecto.Changeset.t(),
  opts :: Keyword.t()
) :: Ecto.Schema.t() | no_return()

Delegates to an appropriate repo determined by DAL.Repo.Discoverable then behaves just like Ecto.Repo.update!/2