How to determine contexts with Phoenix 1.3

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.

4 Likes

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).

1 Like

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.

A relevant quote from dhh.

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.

10 Likes

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 :slight_smile:

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! :slight_smile: 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]

You are wrong :stuck_out_tongue: . Thanks for the feedback!

11 Likes

@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.

6 Likes

This is good! Thinking is harder than not thinking, so some effort is expected :smiley:

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.

7 Likes

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.

4 Likes

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.

It is fun to think of

3 Likes

Hi, you can check out this post. It will help you and me to understand the concept of DDD.

Feel free to share your understanding.

1 Like

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.

2 Likes

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?

1 Like

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 Moebius seems 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.

2 Likes

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.

2 Likes

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.

2 Likes

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?

3 Likes

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.

2 Likes

So you are using the same table but different schema definitions. I suppose i need to try it out to come up with a better understanding.

1 Like

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 :dancer:

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.

4 Likes

How do you specifically handle other data that references the user?

Do you still have foreign keys and use the user_id approach as mentioned by Jose?

1 Like

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:

lib/my_app/user_api/user_api.ex
lib/my_app/user_api/user.ex

I found that having context with a name UserAPI is an absolutely amazing starting point, because:

  1. File user_api.ex is now my dump pipe for all user-related code,
  2. schema file user.ex remains clean and un-touched,
  3. 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,
  4. 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,
  5. 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.

6 Likes