dgamidov

dgamidov

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?

Showing Posts 1 to 10

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.

peerreynders

peerreynders

Sounds like you are talking about bounded contexts. Identifying and maintaining the correct bounded contexts within a monolith is supposed to give some of the benefits of microservices without taking on the inherent overhead (though in the end more discipline is required). The interesting thing is that applying DRY across context boundaries can result in a “mess of coupling”.

David Dawson:

Also InfoQ: Don’t Share Code Between Microservices (2015-Jan-25).

kelvinst

kelvinst

Yep! That’s what I was talking about!

krapans

krapans

Would be cool if you in the end shared this architecture as open source project. :wink:

dgamidov

dgamidov OP

Sure, but first of all we should improve diagram :slight_smile:

gdub01

gdub01

I had a similar plan going.

But I’m wondering where to put both email delivery or file processing / uploading to s3? ACME bank has messaging as a separate module, but wojtekmach mentioned in another post that that repo is to demonstrate what’s possible more than what you necessarily should do. So just wanted to see the general consensus.

File processing / uploading - Depends on checking user permissions (biz logic) and saving newly created s3 URL to the db (biz logic), but the details of where to upload the file and how to process/compress the file are not super tightly tied to biz logic, right? I don’t know. But I’m kind of leaning to keeping file processing in the biz logic part of the app.

Messaging/transactional emails - Biz logic says which emails to send to whom and when… But it doesn’t necessarily need to know whether a message was successfully sent. It could theoretically just tell the messaging app to send a message, then forget about it. So I’m leaning toward that being a separate app.

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 #7
dgamidov

dgamidov OP

I have mail delivery in business logic - it is single point of sending emails in my umbrella application.
If I will change UI or add additional I will still have this functional.

gdub01

gdub01

Interesting! I hope you don’t mind, but I have a bunch of questions =)

It seems like your app must handle p2p payments and/or payment to use the app. So you handle the payment processing in the core app? Like, use stripe or something within the core app, or did you make a separate app to handle that?

I would imagine webhooks come in via your payment processor from time to time? Do you basically forward those directly to your core app?

When a request comes in via your api, where do you put the context plug for graphql? Like, the graphql app needs the context, which is given by the user account’s tokens grabbed from the core app, but is available via the connection in the web app? How did you put that bit together?

How did you end up handling file uploads with graphql? Are you posting to a separate non-graphql route for uploads or setting it in context?

Do your graphql resolvers ever call changesets directly or do they only hit up your services?

Are your graphql resolvers responsible for checking authorizations, or do your services ask for the authorization first, and the graphql resolver just calls the service?

Thanks!

hlx

hlx

Ask away! Small sidenote though, I’m still building it :wink:

It seems like your app must handle p2p payments and/or payment to use the app. So you handle the payment processing in the core app? Like, use stripe or something within the core app, or did you make a separate app to handle that?

Not really sure about this one yet, I would like to put the general (CRUD) payment logic in the core app but I’m still thinking about this one (and fulfilment as well).

I would imagine webhooks come in via your payment processor from time to time? Do you basically forward those directly to your core app?

I think a small router will handle the forwarding / parsing of the webhooks to the core app.

When a request comes in via your api, where do you put the context plug for graphql? Like, the graphql app needs the context, which is given by the user account’s tokens grabbed from the core app, but is available via the connection in the web app? How did you put that bit together?

The api is an GraphQL, Plug and Cowboy app. Any plug that is needed is placed with the app itself. (same for web) Both use the core app to check the credentials and retrieve the user.

How did you end up handling file uploads with graphql? Are you posting to a separate non-graphql route for uploads or setting it in context?

No sure yet, I’ve tested a couple of setups but haven’t found a good one yet.

Do your graphql resolvers ever call changesets directly or do they only hit up your services?

Only the services

Are your graphql resolvers responsible for checking authorizations, or do your services ask for the authorization first, and the graphql resolver just calls the service?

Guardian is used in both api and web to handle the authentication (token, session, etc). Comeonin is used in the core to handle the user related functions like creating the password hash and checking them.

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews