dgamidov

dgamidov

Domain Driven Design diagram for Umbrella app

Hi all!

I am working on umbrella app now - 4 apps in it.

Being impressed with Controller Control: Designing Domains for Web Applications - Gary Rennie and Episode @057: Educating in Elixir with Dave Thomas @pragdave I have started to think about my application design.

I have started to learn these books:
Martin Fowler - Patterns of Enterprise Application Architecture
InfoQ: Domain Driven Design Quickly

Also, @Gazler recommended:
ElixirConf 2016 - Building Umbrella Project by Wojtek Mach and
github:acme_bank by @wojtekmach

Based on it, I have made a diagram:

How can I improve it?

Most Liked

hlx

hlx

I’m trying out the following structure

umbrella
├── core (Elixir)
├── web (Phoenix)
└── api (GraphQL)
├── config
│   ├── config.exs
│   ├── dev.exs
│   ├── prod.exs
│   └── test.exs
├── lib
│   ├── core
│   │   ├── checkout
│   │   │   ├── (...)
│   │   ├── account
│   │   │   ├── authorizers
│   │   │   │   ├── organization_authorizer.ex
│   │   │   │   ├── sales_channel_authorizer.ex
│   │   │   │   ├── shop_authorizer.ex
│   │   │   │   └── user_authorizer.ex
│   │   │   ├── models
│   │   │   │   ├── domain.ex
│   │   │   │   ├── organization.ex
│   │   │   │   ├── sales_channel.ex
│   │   │   │   ├── shop.ex
│   │   │   │   ├── tax_setting.ex
│   │   │   │   ├── theme.ex
│   │   │   │   └── user.ex
│   │   │   ├── repositories
│   │   │   │   ├── organization_repository.ex
│   │   │   │   ├── sales_channel_repository.ex
│   │   │   │   ├── shop_repository.ex
│   │   │   │   ├── theme_repository.ex
│   │   │   │   └── user_repository.ex
│   │   │   ├── organization_service.ex
│   │   │   ├── sales_channel_service.ex
│   │   │   ├── shop_service.ex
│   │   │   ├── theme_service.ex
│   │   │   └── user_service.ex
│   │   ├── inventory
│   │   │   ├── authorizers
│   │   │   │   ├── collection_authorizer.ex
│   │   │   │   ├── permalink_authorizer.ex
│   │   │   │   ├── variant_authorizer.ex
│   │   │   │   └── product_authorizer.ex
│   │   │   ├── models
│   │   │   │   ├── collection.ex
│   │   │   │   ├── image.ex
│   │   │   │   ├── permalink.ex
│   │   │   │   ├── price.ex
│   │   │   │   ├── product.ex
│   │   │   │   ├── stock.ex
│   │   │   │   └── variant.ex
│   │   │   ├── repositories
│   │   │   │   ├── collection_repository.ex
│   │   │   │   ├── permalink_repository.ex
│   │   │   │   ├── variant_repository.ex
│   │   │   │   └── product_repository.ex
│   │   │   ├── uploaders
│   │   │   │   └── image_uploader.ex
│   │   │   ├── collection_service.ex
│   │   │   │── permalink_service.ex
│   │   │   │── variant_service.ex
│   │   │   └── product_service.ex
│   │   ├── relation
│   │   │   └── models
│   │   │       ├── collection_permalink.ex
│   │   │       ├── product_collection.ex
│   │   │       ├── product_image.ex
│   │   │       ├── product_permalink.ex
│   │   │       ├── product_price.ex
│   │   │       ├── product_shop.ex
│   │   │       ├── product_stock.ex
│   │   │       ├── shop_organization.ex
│   │   │       ├── user_organization.ex
│   │   │       ├── variant_image.ex
│   │   │       ├── variant_permalink.ex
│   │   │       ├── variant_price.ex
│   │   │       └── variant_stock.ex
│   │   ├── application.ex
│   │   ├── authorizer.ex
│   │   ├── definitions.ex
│   │   └── repo.ex
│   └── core.ex
├── priv
│   └── repo
│       ├── migrations
│       │   ├── (...)
│       └── seeds.exs
├── test
│   └── (...)
├── README.md
└── mix.exs

In web and api I use the core package like:

{:ok, shop} = MyApp.Core.Account.ShopService.get(1)
{:ok, shop} = MyApp.Core.Account.ShopService.update(shop, params)

defmodule MyApp.Core.Account.ShopService do
  alias MyApp.Core.Account.{ShopAuthorizer,ShopRepository,Shop}

  def update(%Shop{} = shop, params) when is_map(params) do
    with :ok <- ShopAuthorizer.authorize(:update, shop),
      do: ShopRepository.update(shop, params)
  end
end
12
Post #8
peerreynders

peerreynders

Pardon my ignorance but is there a technical reason for for adopting this type of organization:

inventory
        ├── authorizers
        │   ├── collection_authorizer.ex
        │   ├── permalink_authorizer.ex
        │   ├── variant_authorizer.ex
        │   └── product_authorizer.ex
        ├── models
        │   ├── collection.ex
        │   ├── image.ex
        │   ├── permalink.ex
        │   ├── price.ex
        │   ├── product.ex
        │   ├── stock.ex
        │   └── variant.ex
        ├── repositories
        │   ├── collection_repository.ex
        │   ├── permalink_repository.ex
        │   ├── variant_repository.ex
        │   └── product_repository.ex
        ├── uploaders
        │   └── image_uploader.ex
        ├── collection_service.ex
        │── permalink_service.ex
        │── variant_service.ex
        └── product_service.ex

rather than something like this

inventory
        ├── collection_service
        │   ├── collection.ex
        │   ├── collection_authorizer.ex
        │   ├── collection_repository.ex
        │   └── collection_service.ex
        │── permalink_service
        │   ├── permalink.ex
        │   ├── permalink_authorizer.ex
        │   ├── variant_repository.ex
        │   └── permalink_service.ex
        │── variant_service
        │   ├── variant.ex
        │   ├── variant_authorizer.ex
        │   ├── variant_repository.ex
        │   └── variant_service.ex
        └── product_service
            ├── product.ex
            ├── product_authorizer.ex
            ├── product_repository.ex
            └── product_service.ex

Seems the organizing principle is to put “like” things into the same place rather than “putting everything to deal with subject area X” into the same place. One of the starting points described by the article that I linked to earlier - Bring clarity to your monolith with Bounded Contexts - is to “Invert folder structures into a flat domain-oriented grouping”.

The organizing principle of “putting like things into the same place” that ultimately is responsible for “a Rails app always looking like a Rails app” (and not communicating the actual intent behind the web app) is what inspired Architecture: The Lost Years (Ruby Midwest 2011 Keynote). That type of organization is rarely helpful in revealing the emerging (context) boundaries as the application matures (Evans, DDD p.48: “…crucial discoveries always emerge during the design/implementation effort”).

FYI: for anyone interested: Eric Evans: DDD Reference (PDF 2011)

kelvinst

kelvinst

Well, I have a very close setup to yours, the only difference is the “infrastructure layer” you have and I don’t. In my case, the plugs stay on the application layer (because they have web specific logic, like session control and etc), and the service and the repo go to the domain layer, which does not have any kind of web logic, just business logic.

I’m happy with my structure for now, and it’s a pretty clear separation of concerns. My next step, I guess, is to separate the domain layer in more pieces, by resources and features and not by a specific layer itself. Something I learned with CBRA (Component Based Rails Applications) your application is a big box, and big boxes tend to become a mess if you don’t separate into mor boxes, and label them correctly.

Where Next?

Popular in Questions Top

vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
New
Tee
can someone please explain to me how Enum.reduce works with maps
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
mgjohns61585
Could someone help me? I’m making my first elixir program, number guessing game. I can’t figure out how to convert the user’s guess from ...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
dotdotdotPaul
Okay, I’m having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I’m sure I’...
New
yawaramin
In the Dialyzer docs ( dialyzer — OTP 29.0.2 (dialyzer 6.0.1) ), there is a way to turn off a specific warning for a function: -dialyzer...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New

Other popular topics Top

sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New

We're in Beta

About us Mission Statement