You can use contexts to fullfil the DDD principles, but you donât need to - it all depends on how youâre going to use them in your application. If you deem it appropriate to adhere to DDD - go ahead. If you donât, all you have to do is make explicit functions for all the operations that are allowed in your app, instead of sprinkling Repo and other services access over controllers.
Thatâs something @chrismccord also says in his keynote - contexts are inspired by DDD, but are not inherently tied to it.
Thank you! I will watch this keynote one more time (there is no subtitles and hard to understand sometimes). I just bought Eric Evans BBB already. So I will split to modules (like normal Elixir w/o DDD) and maybe refactor later (after reading).
The more I read about contexts the more I think itâs a bad idea.There seems to be a big push for âPhoenix is not your applicationâ which, as far as I can tell, is nonsense. Itâs a very big anti-rails movement.
Phoenix is your application. Itâs a framework that serves as the foundation which you build on top of.
Ecto, Plug e.t.c. are libraries which are not your application. You use them as libraries and as such they donât impose any structure. Youâre free to do use them as you please.
It doesnât help that the two main people behind Phoenix and these changes propose this complex new structure and both reply with the same âoh no we canât give examples because it all depends on what youâre building.â I donât mean to sound too critical but this screams change for the sake of changing.
What we have now is a situation where we have multiple isolated contexts in the same app but are discouraged from actually using them as a whole. e.g. we shouldnât have relationships to other contexts in schemas. This is pretty backwards because if you have these in the same app you are certainly going to want to use everything together.
I personally donât think that contexts is the right solution. I also donât think we actually had a problem in the first place. If you really need separate isolated contexts then they donât even belong in the same app.
Contexts as they are now are a bastardization of DDD and the repository pattern.
Umbrella apps are where the attention should have been focused. Fully separate apps where necessary and a web app that ties them all together.
If I am wrong then please tell me. I realize that I sound very negative but I assure you that Iâm very open to ideas.
Contexts are literally well named modules and functions, and we ask you to think about the API boundaries are you are building your well-named modules an functions. Thatâs it. If you think thatâs a bad idea, then you are saying the way we all build functional programs is a bad idea
When you build a phoenix project, it literally is an âOTP applicationâ. This conflation w/ the above tweet ignores a lot of details about the elixir and ruby worlds. In Rails, your project is a special directory structure tailored to a Rails application. The entire program is started and stopped according to special rails init code. Contrast that with the way we build Elixir applications, they are all OTP applications, with standard start/stop mechanisms and explicit application relationships. âPhoenix is not your applicationâ absolutely applies here and pushes folks to think differently about Elixir as a platform, since itâs unlike what theyâve been exposed to before.
Baby-steps! This is exactly what mix phx.new my_app --umbrella does. [quote=âsync08, post:15, topic:4367â]
If I am wrong then please tell me. I realize that I sound very negative but I assure you that Iâm very open to ideas.
[/quote]
@ssbb You are tying 1.3 to DDD literature too strictly. You are right that contexts really are about module organization, asking folks to think about isolating their features behind well named modules and functions
Both. First and foremost, we are asking folks to isolate functionality behind modules and functions, but we can then point at DDD and say look, if you have multiple representations of a âuserâ in the system, we can apply these ideas from DDD to avoid giant entities in the system with implicit coupling everywhere.
This is good! Thinking is harder than not thinking, so some effort is expected
Itâs important to note, that the generators are learning tools only. Saying you couldnât use version 1.3 on a project doesnât make sense because phoenix is not stopping you from writing any kind of code you want.
This could be helpful, but itâs tricky because 1.3 is asking you to actually think about designing your system, and the Phoenix team could try to dream up realâish apps, but ultimately we canât design everyoneâs application for them. Some larger examples for the community could be helpful, but itâs not something the Phoenix-core team itself should focus on.
Phoenix is not the foundation to the application. Itâs the foundation of the web layer, which is part of your application. E.g. the website I talked about above. All itâs functionality is accessable through the actual website powered by phoenix, but also via iex bypassing everything phoenix is doing. Itâs an elixir app, which includes phoenix to make serving web requests easy. And itâs not like phoenix would offer anything more I couldnât use because of that.
Just to buttress your point, I saw three days ago, a demo of elixir, cowboy and react. It feels so sweet how powerful elixir is. I love it. The app is so fast.
We can actually use a different layer to the web without using phoenix.
However, phoenix is an application inside series of applications going by the concept of OTP.
Phoenix is not your application but one of the applications.
No, its really not. If your notion of how to model an application begins and ends with the surface area provided by Phoenix (or Rails for that matter) then Iâd urge you to dig quite a bit deeper, because youâve got a long way to go.
Phoenix is a mediator between the interface (HTTP/websockets) and your application. It gives you a place to move data from one boundary to another. You can cram code into that seam all you want in the name of simplicity, but donât act like nobody ever told you it was a bad idea.
Yes, thereâs no middle ground between modularity-via-application and the framework-bound monolith.
Iâm not altogether wild about the generator changes, but in fairness to the team, I havenât used them much eitherâI just create my modules as I need them. Iâm not really sure how you hold up a Rails-style application structure as the way to go on one hand, then point to DDD with the other. I think the terminology used in Chrisâs keynote comes pretty well loaded and thereâs a lot of people coming into Elixir and Phoenix with some degree of confusion wondering if they need to bite off the absolutely massive mental undertaking that is understanding DDD and how to apply it, and thatâs unfortunate. Because the essential point is absolutely sound: Modularity is more than just a namespace or code geography, and thereâs more tools to achieve it than a completely separate application.
In the example linked in that blog the âridesharingâ context Trip model access âindentityâ context User model. Is that the way to use shared resources?
I agree contexts are a bad idea, but for the opposite reason as @sync08. If Phoenix is just the web layer, then why is it trying to have any influence over what the rest of my application looks like? I donât like howâif Iâm not intentionally carefulâEcto manages to easily seep outside of data persistence and into other aspects of my application (I havenât tried it yet, but Moebiusseems like it could be better alternative to Ecto from an application design perspective).
To me, the ideal Phoenix and associated resources would look something like this: a Phoenix that focuses only on the web concerns of my application; a database library with a simple and intuitive syntax that does not try in any way to manage the shape of my data at any layer of my application other than between the library itself and the database (no schemas or changesets, yes on migrations); and perhaps a validation library that also does not try in any way to manage the shape of my data (although in many circumstances data validation can be done easily with only the stdlib). None of these three key libraries would make any assumptions regarding the existence of the others.
I am (of course) immensely grateful for the thought and effort that the Phoenix and Ecto teams have put into the Phoenix 1.3 and Ecto 2.0. I do feel (strongly) that contexts and schemas were not the right way to move away from models.
The only thing that is trying to have influence on the rest of your application (and we could discuss how much) are the generators and they are meant as a learning tool not something that decide all your stuff.
In particular, this move is exactly about that. Before Phoenix was trying to give you âmodelsâ and âControllersâ that did things. All that is out now, and it let you do as you want.
The generators, the to be updated guides (I assume), the to be updated books and new books yet to come (I assume), presentations from the creator specifically regarding the design of Phoenix applications, and last but certainly not least the community.
None of these things are physically forcing my fingers to hit my keyboard in such a way that they produce a context-schema Phoenix application, but they have strong undeniable influences on the support and development of resources and an environment favorable specifically to the context-schema application design.
How do you handle common models. Like registration would create a user, which would then be used in auth, ticketing etc. Do you have a User schema under Auth which is then used by other contexts?
Iâve a custom User schema for registration, which does hold information e.g. related to email confirmation opt-in. That one is not used by any other contexts. Thereâs also a User schema in auth, which does use the same table as the registration one, but loads different data. That one is used for auth and ticketing and such, so basically for everything done by the âcurrent userâ. But besides for the login/auth plug Iâm not even concerned with the auth context from the phoenix pov. Iâm simply passing the current user into the ticketing context and itâs retrieving the needed data of of the struct. So thereâs not really interdependency between the contexts, but itâs rather passing data around. Itâs like when I pass the ticket data to the pdf context to let it render the ticket voucher.
As @DianaOlympos said, Phoenix already ticks those boxes. The thing you are complaining about as dictating how you write your applications are learnings tools to help push folks in the right direction as they are ramping up. You are right that these ideas from the generators will permeate outside, but if âwell named modules and functions, with clear isolated boundariesâ is what ends up having âstrong, undeniable influencesâ on folks code, then MISSION ACCOMPLISHED
I think Phoenix 1.3 actually aligns with your desires if you take a step back. Phoenix 1.3 is all about making it clear that Phoenix is just the web interface to your greater application. Along with that, to have a âweb interfaceâ, you need something to interface with, namely well-named modules and functions that accomplish the concerns of your domain. This is where the generators and contexts come in, to help push folks along as they are getting up to speed. If you donât like particularly features like the generators â donât use them. If you arenât happy with Ecto, use something else, Phoenix does not care. All integration with Ecto happens via our FormData and Param protocols.
Contexts had me confused as well, I still donât understand them fully. Iâve settled on something simple that works for our application and is good enough.
When starting new application, I needed a simple CRUD for users. To jump-start, I used:
mix phx.gen.json UserAPI User users email:string password:string scopes:array:string
Among other things, I got this folder with two files:
I found that having context with a name UserAPI is an absolutely amazing starting point, because:
File user_api.ex is now my dump pipe for all user-related code,
schema file user.ex remains clean and un-touched,
I use the user_api.ex dump pipe until I figure out some of the code is good to go into a module of itâs own,
As soon as I figure out something needs to be extracted, I created a file nested under user_api; recently I added a file called user_api/search.ex that incorporates validating search params & building complex search queries for my users,
My user_api.ex is now a glue between repo & user_schema and the rest of the app: by our convention no module should be calling functions from MyApp.Repo/user_schema directly.
This maybe be very dumb and simple, but it works perfectly for us.