pedromtavares
Hey everyone,
After some discussions here on the forum, I decided to write a blog post about structuring a Phoenix app with a big focus on properly layering your domains: Blazing with Phoenix: Project Structure - DEV Community
I’m really curious to know how other people do this as I struggled to find material when I was tackling this problem myself, so hopefully this discussion will provide further guidance to Phoenix newcomers.
Trending in Discussions
As the title says, please share what you’ve been up to with Elixir. Whether that’s been learning it, looking into it, making stuff with i...
New
The obligatory hello world thread!
Who are you and where are you from? :stuck_out_tongue:
New
I want to open this thread for you all to discuss and help those who really like Ash but are still hesitant to use it in a real project. ...
New
I was working on an Ecto migration and I needed a timestamp. So, for the nth time, I looked up the different data types for timestamps, a...
New
Fly’s CEO posted this recently - Turn And Face The Strange · The Fly Blog
It says that Fly is going all-in on sprites, which is a worry ...
New
We’re evaluating API mocking tools for OpenAPI-based projects and would love to hear what other teams are using.
We’re particularly inte...
New
Is there a word for the ~> symbol used in Version strings?
Do you also just call it a Squiggle Arrow™ ?!
New
Other Trending Topics
Hobbes is a low-level distributed database for the Elixir programming language.
Hobbes provides a simple, safe, and scalable storage lay...
New
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve.
They are GUI (Emerge) and State management (S...
New
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
There are three potential reasons for members of this forum to have a look at https://vutuv.de
You are tired or annoyed of LinkedIn.
Yo...
New
Latest Phoenix Threads
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #blog-post
- #ai
- #phoenix_html
- #iex
- #elixirconf-us
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming










Showing Posts 1 to 10- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
BartOtten
This is the way I structure my apps too. To avoid a lot of boilerplate, I am using macro’s to create many default functions (CRUD for example) which can be overridden when needed.
The blog post is a good explanation for newcomers struggling to find a good structure
baldwindavid
Really enjoyed the article. This is how my app is structured as well. It took me a long time to arrive here and wish I would have read something like this early on. There is a chance that I did, but that it didn’t click yet.
I don’t know that this is much of a difference, but for cross-domain orchestration I have been creating dedicated service/use-case modules for cases where more than just a couple lines of code are needed. These are located in a “/services” directory for lack of a better term. The top-level domain then delegates to those functions. Do you have any sort of convention for more complicated cases?
I also wonder how much you are thinking about the direction of dependencies between your contexts. Most of my contexts can query my
Accountscontext, but the opposite is not true. I try to enforce a hierarchy amongst my contexts. I haven’t yet used the Boundary library yet, but am very intent on exploring this to avoid tight coupling and circular dependencies.@BartOtten - I’ve specifically tried to avoid macros and such for context functions until I have enough knowledge/examples of how my contexts typically shake out. I’m now at the point where I have a lot of ceremony and duplication. It is easy to read, but I’d like to push some of the things that I do every single time to macros for readability and to cut down on the need to write the same tests over and over. If you have any specific examples of macros that you would like to share, I’d love to see them.
pedromtavares
Great to hear!
Yeah I think the idea of moving larger orchestration to its own module is fine, I place mine as direct children of the app level domain,
App.Conductorfor example. I think moving these to their own folder would stimulate filling this folder up, and if you start having too much of these then maybe that’s a sign that the current contexts aren’t very well abstracted, as the bulk of work needs to be moved downwards, not sideways.Forcing hierarchy amongst contexts is exactly what needs to be done, this way you create more layers of abstraction and avoid the problem that I mentioned above. I haven’t used that library either but really like the idea behind it, definitely helpful for teams to stay consistent with domain encapsulation.
Really appreciate the thoughtful post!
tfwright
Very nice walk through! This is definitely something I wish I read when I first started trying to wrap my head around the Phoenix ‘contexts’ concept.
I found it very interesting that you start with an ‘app’ context (the
Rpgmodule) and then introduce more conventional contexts as a layer between that and the schemas. I really like that from an “agile” perspective because you don’t have to decide about what divisions will emerge in your domain before you’ve done more than add a single schema.Conversely though, on the idea of starting with query and services modules, I wonder if this caveat at the end of the article doesn’t also apply:
Splitting schema logic into multiple modules seems to me like an optimization on par with maintaining separate layers of contexts like you do in your example. After all, why not start with one app context? If, or rather, when, your app context needs to be broken up into multiple “contexts” do that. But doesn’t the same apply to the schema module? If your schema is complex enough that it makes sense to break out all the query functions and all the service functions then by all means, do it. But why before? In your example, you start with the former optimization but not the latter, which is opposite to the conventional Phoenix approach, but I am inclined to take the most minimal approach from both first: 1 app context, 1 schema. Go from there.
To play devil’s advocate, it seems like Phoenix introduces the context convention somewhat aggressively in order to firmly dissuade the bad practice of putting everything in your schemas in a Rails-esque way. Part of what your article seems to imply is that maybe it should also encourage starting with a query and services module for the same purpose. But short of some framework level mechanism that makes the dev’s job easier as a result, I’d rather leave those decisions to the architect.
pedromtavares
Thank you!
When writing the post, I decided to focus on one convention at a time, first the Service/Query/Schema split, and then the top-level domain structure, not necessarily that you should follow that order when building your app. The important message I wanted to convey was the final structure, if you have an idea of how it should be in the future, then how you choose to start is also completely up to you.
I personally like that Phoenix introduces Contexts more aggressively, this raises the overall level of most Phoenix apps because people will get interested to learn about them and thus be more prone to make proper architectural decisions.
tfwright
I do agree with this. I think Rails would have been better served by making model namespaces a stronger convention instead of falling into the global services concept.
hlx
I’ve read the article and I was wondering if you have considered ‘defdelegate’ for things like ‘Accounts.create_user/1’
pedromtavares
Not having seen much of this keyword being used nor used it myself, I wasn’t sure of its pros and cons. From what I’ve seen it’s just an extra touch of syntactic sugar, so I wonder if it’s important to introduce it as part of a project convention.
hlx
There is a great discussion on that here Is defdelegate necessary? - #2 by Eiji
Personally, I think that less code is better. Less changes for bugs, less to maintain. (Generally speaking ofc) Aside from the fact that I wouldn’t use layering with contexts, if I had to do it I would do it with defdelegate to simplify the code base.
I have to admit that I need to read your article better and I also want to check out the moba code base but for now I do not see a reason why I should put the Users context under the Accounts context. I’ve worked in many larger codebases over the years and I’ve found that almost always spreading out code over multiple layers can be very hard to maintain in the end. This is a personal preference though and it might not apply to anyone else.
pedromtavares
I’ll start using it in my projects from now on and see how it plays out, will edit the article later if it does make things easier. Not having to retype the whole call is definitely better, especially when you have default values.
I’m curious to how do you structure these larger codebases, do you place Users as a standalone top-level domain? If you could write a small gist to lay out the contexts for us to discuss that would be fantastic.