I posted this thread a little while ago now, but this still something I regularly ponder. As my product and team grows in size, it’s becoming less and less of a hypothetical / thought experiment, and more and more of an actual problem that necessitates attention.
I’m still playing in hypothetical land, and I admittedly haven’t gone back and re-read this whole thread immediately before writing this, so I may be misrepresenting some viewpoints. Forgive me, this has turned into a big thread! If I have done so, I apologise. It is not done in malice. I’m not picking sides. I am (still) not sure enough of myself to do that!
Anyway!
My main hypothetical issue / concern with a hybrid approach is that it does not address a key desire of mine, which is a consistent developer expectation when working on a project.
The appeal to me of a strict services layer is that each routine is an intentional and considered means of interaction with the system. Which I guess might be obvious. It’s very…DDD.
Django does a whole lot for you. That’s its intention. I am sure that we are all on board with this as a benefit—at least in some or even a lot of situations—else we’d probably be using something else. The flip side of this is that when you, for example, create a new model, you need to consider all the entry points that Django is going to provide, and whether they are all going to take into account your domain / business logic.
I personally do not believe that Django’s architecture exactly makes this a cake walk. We could talk all day about whether or not I am right or wrong. In fact, there are plenty of people, including ones I deeply respect the professional competencies of, including people in this thread, who seemingly disagree with me here. I’d be the first to say that this belief could very well be me not knowing enough about Django. However as someone that’s been working with Django full-time for nearly a decade now, I’d argue that this is at the very least not easy or intuitive to do.
The reality of Django, as with all frameworks, is that making assumptions is necessary to provide the abstractions that it does. This means that certain hooks / entrypoints which Django intends for you to insert your unique business logic may not be suitable / capable of allowing this in your unique situation. This means that more complex Django projects may either override more and more of Django’s batteries (which introduces its own complexity which has to be argued for), or may instead introduce rules / conventions—whether implicit or explicit—pertaining to which Django ‘batteries’ you should and should not use.
My issue with a ‘hybrid’ approach (in the way that I’ve interpreted it, which again could be wrong) is that it would necessitate some complex conventions / rules around how you should accomplish various things in various circumstances. This makes it harder for new developers to jump into a project with the confidence to get stuff done. Hell, even someone deeply familiar with the project could forget gotcha A or footgun B. Is it easier to say “only use ModelForm
in these circumstances?”, or simply “we do not use ModelForm
”?. I’d say the latter. A big thing for me here is model validation. If you can’t get Django model validation to do what you want in all circumstances, then the contract is broken, and this is something that needs to be communicated. This isn’t a unique fault of Django’s by any stretch. But the reality is that it’s something that needs to be considered in a project.
We could also talk all day about why Django is this way. Like all things i believe that it is a combination of Django’s origins as essentially publishing software (though it is far far more than that now), ensuring backwards compatibility, required effort, and that addressing this enterprise-level architectures is not as high a priority for the largely-volunteers that work on Django. Which is fine!
As I said up front, I’m not sure enough of myself to want to pick a side. However when it comes time to start more purposely addressing this at my day job, I’m erring on the side of a more comprehensive services layer.