wojtekmach

wojtekmach

Hex Core Team

Thoughts on single context Phoenix apps

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?

Most Liked

peerreynders

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.

mkaszubowski

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.

hubertlepicki

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/ui and apps/backend. In ui I make another heresy of getting rid of the web subfolder and just putting everything straight to lib. I even skip the lib/ui. Everything in this project is scoped within Ui.* namespace, I don’t need a Ui.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/backend that is generally a place for the business logic. If I feel like separating the some things more, I’d create apps/whatever in addition to backend. Usually I’d extract the schemas and abstract the database read/writes and queries. I’d make apps/storage and expose some interface to backend, which in turns exposes some interface to ui. It’s an uni-directional flow where ui has no idea of internal data structures and implementation of backend, which in turns has no idea about internal data structures (including schemas) of storage. Usually there’ll be a few more apps in apps/ 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 the backend’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 :wink:

Where Next?

Popular in Discussions Top

PragTob
Hello everyone, I know we had quite some threads (read through lots of them) about background job processing but it remains a hotly deba...
New
axelson
Decided against including more info in the title, but the gist is that Plataformatec sponsored projects will continue with the assets bei...
New
sashaafm
Piggy backing a bit on @dvcrn topic BEAM optimization for functions with static return type?, I’ve been trying to understand in a deeper ...
New
CharlesO
Erlang :list.nth simple, but 1 - based nth(1, [H|_]) -> H; nth(N, [_|T]) when N > 1 -> nth(N - 1, T). Elixir Enum.at … coo...
New
AstonJ
Please see the new poll here: Which code editor or IDE do you use? (Poll) (2022 Edition) It’s been a while since we first asked this, I...
208 31142 143
New
rower687
Hi all, I’ve been reading a lot about the “let it crash” term and how supervising processes and the whole messaging passing make an elixi...
New
jer
I’ve been using umbrellas for a while, and generally started off (on greenfield projects at least) by isolating subapps based on clearly ...
New
shishini
I think this twitter post and youtube video didn’t get as much attention as I hoped I am still new to Elixir, so can’t really judge ...
New
und0ck3d
Hello everyone! A few days ago I’ve created a topic here about how people were creating CMSs with Elixir and Phoenix. I’ve been studying...
New
AstonJ
If so I (and hopefully others!) might have some tips for you :slight_smile: But first, please say which area you’re finding most challen...
New

Other popular topics Top

vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
johnnyicon
Hi all, I’ve just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I’m trying to use Postgres...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New
RisingFromAshes
I’ve read in another post that it may be possible with a router helper - but I couldn’t find an appropriate one, and tbh, I’m still just ...
New
komlanvi
Hi everyone, I was playing with phoenix liveView but I run into an issue. I have a form and want to validate each input text when the te...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
dogweather
I wrote this comment on r/haskell, and it’s not popular there. :wink: But I think I’m on to something… Haskell reminds me of Java, and e...
New
svb
Hi! Currently I want to submit a form by pressing the Enter key. However, since my input field is of type “textarea” this is just adds a...
New
sergio
Kind of like when jquery came out, it was super necessary. Existing drag and drop libraries have a bunch of baggage to support old browse...
New

We're in Beta

About us Mission Statement