wojtekmach
Hey! I vaguely remember that someone (maybe @chrismccord?) mentioned that small Phoenix apps can get away with just a single context which is the app itself. This is exactly how I often build small experiments.
Here’s an example: Commits · wojtekmach/twittr · GitHub. Note, we have: Twittr.list_statuses(), Twittr.create_user() instead of traditional Twitter.Statuses.list_statuses() etc. Minor change, but I find it cleaner and more convenient to have everything in one file early on. When I don’t have a clear idea how to name the context or what put it there, I just put stuff into the “app-context” and treat it as a “staging” area. Over time, stuff emerges and I extract it out.
While it might be counter to spirit of “intentional design” (since there’s just one context there’s not much design there, other than I guess naming functions properly), it helps me from “design paranoia” where I’m blocked on naming things.
It works pretty well for me, I’m curious if this is something worth encouraging people to do more often, or seems counter-productive?
Trending in Discussions
Other Trending Topics
Latest Phoenix Threads
Chat & Discussions>Discussions
Latest on Elixir Forum
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
- #phoenix_html
- #iex
- #graphql
- #genstage
- #ai
- #elixirconf-us
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex










First 10 of 14 Posts
hubertlepicki
There’s no one size fits all. If this fits you, and your style of coding go and encourage that. But make others aware that it’s not the only true way. Damn, “this is the Rails way” made so much damage to Ruby projects that it’s painful to think of it.
Luckily, both Elixir and Erlang are more flexible. You cn use contexts, sure. I just call them namespaces / modules, and do not follow the convention at all. I’d normally namespace my services, sure, but I would not namespace either controllers (and templates, views) unless they’re nested in router. I would not namespace schemas either. Controllers and schemas map to routes and database tables, and does not make sense in my head to namespace them in contexts having some extra meaning.
I do like another abstraction that comes with OTP, which are the applications. And I tend to use them more heavily than the others I think. What I would normally do when starting a project would be to start with umbrella project and two apps in it.
apps/uiandapps/backend. InuiI make another heresy of getting rid of thewebsubfolder and just putting everything straight tolib. I even skip thelib/ui. Everything in this project is scoped withinUi.*namespace, I don’t need aUi.Web, nor extra directory. It’s hard to reach to the files with such long paths even with Ctrl+P. That’s okay, it’s my project and I decide.Then I’d put stuff to
app/backendthat is generally a place for the business logic. If I feel like separating the some things more, I’d createapps/whateverin addition to backend. Usually I’d extract the schemas and abstract the database read/writes and queries. I’d makeapps/storageand expose some interface tobackend, which in turns exposes some interface toui. It’s an uni-directional flow whereuihas no idea of internal data structures and implementation ofbackend, which in turns has no idea about internal data structures (including schemas) ofstorage. Usually there’ll be a few more apps inapps/folder. Logs handling, watchdogs, handling integration to external services and other infrastructure bits would find it’s own apps.And then there’s the kicker in form of testing the layers in separation. Right now, I can test my
Ui, stubbing out thebackend’s functions as needed with mox, and test my business logic, stubbing out the database read/writes.So the above is the reason I like applications. I don’t mind if there’ll be dozen of them in
apps/.That’s fine. Sorry for lengthish reply and off topic, this been on my mind for some time and had to share
peerreynders
I don’t think so. You are simply supposed to use all of the information that you can have before starting to formulate your starting position. That means putting in some genuine effort beforehand into understanding the domain that your application is supposed to be solving some problem in. That being said you likely won’t have all the information that you need up front. Trying to divine all the necessary knowledge up front is what leads to analysis paralysis.
Small experiments by definition have a very limited domain and a very tight focus - so you don’t tend to separate things.
This means you are still in exploratory/discovery mode. There is nothing wrong with that. The problem is when people only explore things in code - possibly bypassing the (cheaper) opportunity to talk to people who are dealing with the problems today as that conversation may shed some light on the inherent structure and forces within the problem domain that may help inform your initial design.
And this is the part where many fail. Project hygiene requires that you don’t let separate concerns/responsibilities “cohabitate” in the same place for the sake of convenience as they will tend to “complect” leading to accidental complexity. Refactoring is essential to manage that complexity. Hence the mantra “refactor early, refactor often”.
The problem is that refactoring is real effort. During that time and effort there is no visible progress “from the outside”. So people put it off - taking on technical debt. The longer the refactoring is put off the greater the refactoring effort will be, especially as new features are built on top of the sub-optimal foundation accruing additional rework - and that is how the big ball of mud starts rolling and growing.
So it comes down to seeking out information that is available to you before you start and make a “plan” (the design). However this isn’t a “create a plan and stick to it” kind of situation but instead the planning process is about exploring (and being familiar with) all the available options and choosing the best path at that time.
When the plan (design) is implemented, typically more information comes to light, some options vanish and new ones pop up and the “optimal path” changes and the plan needs be adjusted accordingly (i.e. Refactoring time).
In my experience one of the hardest things to refactor are database schemas (especially when there is live data out there). So typically I like to keep “schema knowledge” out of the code so that it is easier to refactor in the future.
MikeAnthony
“While it might be counter to spirit of “intentional design” (since there’s just one context there’s not much design there, other than I guess naming functions properly), it helps me from “design paranoia” where I’m blocked on naming things.”
One good thng about contexts is that you are not forced into using them one way. I really get annoyed when programming purist try and pretend that their concerns and design patterns apply to every one and every situation. Its hubris. They don’t . You are probably fine. the better approach is to ask you some questions.
Are you developing an app that you know will be in service for years to come?. Then perhaps Perry above has a point. You salary is sure and the company is committed for years to it. They probably want you to take your time amd even obsess over seperation of concerns etc
If however you are a startup working on a proof of concept Perry’s point would not apply. You simply cannot afford to have “no visible progress from the outside”. When you obsess and lose investment or market opportunity then “hygiene” won’t matter a wit - the patient is dead.
I find myself in that category with my present projects . I am either working on my startups or working for companies and business who have tight deadlines that determine their future. I’ll let separate concerns/responsibilities “cohabitate” in the same place for conveience every day and the weekends if it gets my clients app to production where he can maximize an opportunity. design purity (whatever that is) be damned.
If like me you came to Elixir for its high concurrency thats even MORE the case if its a new idea startup. Sure Phoneix willperform great if you break out but the idea might just as well flop and fail to get the traffic you chose Elixir to handle. Keep your eye on the prize. get the app out the door and working because if it does take off there will be money to refactor till your hearts content but no investor or market is going to pour in the money because its design is “pure”.
If that app is secure, works and performs well and satisfies its users then do it with one context two contexts or whatever gets you there. You will be fine. Living projects can reorganize. dead ones can’t.
If its a hobby project then I’d probably explore contexts fully and try and build ones with more than one just for the learning experience
mkaszubowski
I also think that this might be a good idea, especially in the early phase of the project, when you are not sure how various parts of your system connect and interact with each other.
From what I’ve seen so far, starting the project with multiple contexts often leads to model-like contexts which all just have the same set of functions (list, get, create, update, delete) and correspond to the underlying schemas and database tables. Having one global context can help you understand what the application does instead of what the data looks like. It’s especially valid in Elixir when you can easily refactor by moving functions between the modules without having to worry about the state and side effects.
dimitarvp
@MikeAnthony Cannot find anything to disagree with you. Practicism and current circumstances > formulas. Always.
That being said, I discovered that when I pursue even half purity while progressing even a time-constrained project, it often times unlocks my brain in ways I could not predict. If you use the wrong design then you are burdening your mind with the wrong idea and thinking about how to solve problems inside that becomes difficult with time.
But that only applies for mid-to-long term projects, namely from 4 months and on. If you do a lot of short-term contracting then the above definitely applies much less (if at all).
dimitarvp
Yep, seen this in several languages, Elixir included. Then I am onboarded and I am just “WTF?” and ask the person who authored it what is the purpose this thing serves outside of just having a separate module with DB-specific code. Sadly often times people cannot explain because they simply follow recipes.
Not sure if any programmer celebrity ever said this but ever since I work with FP languages, I religiously pursue minimum file count. Context modules, as everything else, must have a very good reason to exist before being created.
jordiee
I personally like the idea of only using Phoenix as a communication interface and the actually logic/work is done in a separate application that is called from phoenix. I have an application I am working on currently that is a single phoenix application that has 7 “sub-applications” in its mix.ex file that actually handle all the logic/work.
dimitarvp
I am fully with @hubertlepicki here and it’s a shame we are not colleagues. We would work perfectly together.
YourApp.Schema.User. It’s a trained brain trigger that this is a framework / gluing module. Depending on project complexity, naming likeYourApp.Schema.RDBMS.UserandYourApp.Schema.KV.Avatarcould work as well.YourApp.Order.cancel_order/1function that knows about the steps and calls other modules (most of the time storage-centric ones) but has no idea about the storage itself.Additionally, minimalistic directory depth. Not sure if umbrellas allow that but I would shorten all paths of the kind
apps/storage/lib/storage/module.exto justapps/storage/lib/storage.ex. I don’t expect to have two apps inside an app that is already a part of an umbrella app so these default paths don’t make much sense to me. There could be a good reason for them though.Another thing I would do is change
lib/yourapp/application.extolib/yourapp_application.ex. I feel like the OTP wiring belongs on as a higher level as possible.And finally, minimum amount of files. Deleting the default
lib/yourapp.exis one of the first things I always do when starting a project. If I really need such a central point in my code then the need will be already known in the future and the file will have good reasons to be recreated.EDIT: Regarding the minimum amount of files, I also mix my
GenServerwiring and the actual functions that do cast / call to it. EveryGenServeris, generally speaking, a stateful helper and a gateway to side effects, so I see no reason to split it in two files.dimitarvp
That’s also exactly what I do. Be it a website (Phoenix), or a REST API, or GraphQL resovers, they all should just be wiring / boilerplate that connects to the real actions that reside elsewhere in your app forest. And those real actions should be storage-agnostic and endpoint-agnostic. They should not care if ther results are served through a JSON firehose, a total JSON request, XML objects, streamed to a GraphQL client, or whatever else.
AstonJ
I love how Phoenix and Elixir offer you the flexibility to do things how you want. It’s awesome that we have at least three routes emerging as being so widely used that you can feel confident about using them, knowing that there will be others who can help you in making decisions or if you get stuck as the concepts won’t be alien to most people in the community.
I actually think this is one of Elixir’s killer features; there’s not just - like in some other languages or frameworks - a single “____'s way”, there are several! And several that have got the support of the community after having been discussed and identified as a great way to architecture and build certain kinds of apps.
Phoenix is so flexible that you can use it in several ways:
It doesn’t stop there either - Phoenix is so flexible that you can even have multiple Phoenix layers in an Elixir app - imagine trying to do something like that in Ruby
Thank you Chris, José, Dave and everyone else who have helped make all this possible - I really do think it’s one of Elixir’s killer features





