Skip to content

Commit

Permalink
get appendix django into better shape
Browse files Browse the repository at this point in the history
  • Loading branch information
hjwp committed Jan 14, 2020
1 parent 92c3e87 commit e1beca9
Showing 1 changed file with 53 additions and 70 deletions.
123 changes: 53 additions & 70 deletions appendix_django.asciidoc
Expand Up @@ -151,9 +151,10 @@ class DjangoRepository(AbstractRepository):


You can see that the implementation relies on the Django models having
some custom methods for translating to and from our domain model.

// TODO: point people at https://mappers.readthedocs.io/en/latest/
some custom methods for translating to and from our domain model.footnote:[
The DRY-Python project people have built a tool called
https://mappers.readthedocs.io/en/latest/[mappers] which looks like it might
help to minimise boilerplate for this sort of thing.]


==== Custom Methods on Django ORM Classes to Translate To/From Our Domain Model
Expand Down Expand Up @@ -365,30 +366,34 @@ def allocate(request):
====


=== Conclusions: Would You Bother? What else can you do?
=== Why was this all so hard?

OK it works but it does feel like more effort than Flask/SQLAlchemy. Why is
that, and when might you still choose Django?

- it's hard because the ORM doesn't work in the same way. We don't have
an equivalent of the SQLAlchemy classical mapper, so our ActiveRecord
and our domain model can't be the same object. Instead we have to build a
manual translation layer behind the repository instead. That's more work
(although once it's done the ongoing maintenance burden shouldn't be too high).
that?

- it's also hard because you need to integrate `pytest-django` and think
carefully about test databases etc
The main reason at a low level is because Django's ORM doesn't work in the same
way. We don't have an equivalent of the SQLAlchemy classical mapper, so our
ActiveRecord and our domain model can't be the same object. Instead we have to
build a manual translation layer behind the repository instead. That's more
work (although once it's done the ongoing maintenance burden shouldn't be too
high).

So why might you still do it?
Because Django is so tightly coupled to the database, you have to use helpers
like `pytest-django` and thinking carefully about test databases, right from
the very first line of code, in a way that we didn't have to when we started
out with our pure domain model.

* when migrating an existing project that has Django?
But at a higher level, it's because the entire reason that Django is so great
is that it's designed around the sweet spot of making it easy to build CRUD
apps with minimal boilerplate. But the entire thrust of our book is about
what to do when your app is no longer a simple CRUD app.

* or because you want the Django Admin? But we'd have to say that's likely to
be a bad idea--it goes against the grain of wanting to decouple your model
and business logic from the ORM. The Django admin makes sense for CRUD
apps. If you have a more complex domain + workflow, it's less likely to
be useful.
At that point, Django starts hindering more than it helps. Things like the
Django Admin, which are so awesome when you start out, become actively dangerous
if the whole point of your app is to build a complex set of rules and modelling
around the workflow of state changes. The Django admin bypasses all of that.

=== What To Do If You Already Have Django

So what should you do if you want to apply some of the patterns in this book
to a Django app? We'd say:
Expand All @@ -399,76 +404,54 @@ to a Django app? We'd say:
decouple your app from Django and the database, so if you anticipate wanting
to migrate away from either of those, Repository and UoW are a good idea.

* Service Layer might be of interest if you're seeing a lot of duplication in
your views.py. It can be a good way of thinking about your use cases,
separately from your web endpoints.

* You can still theoretically do DDD and domain modelling with Django models,
tightly coupled as they are to the database; you may be slowed down by
migrations, but it shouldn't be fatal. So, as long as your apps is not too
complex and your tests not too slow, you may be able to get something out of
the "fat models" approach: push as much logic down to your models as possible,
and apply patterns like Entity, Value Object and Aggregate
and apply patterns like Entity, Value Object and Aggregate. Although see
caveat below

With that said,
https://forum.djangoproject.com/t/where-to-put-business-logic-in-django/282/7[word
in the Django community] is that people find that "fat models" runs into
scalability problems of its own, particularly around managing interdependencies
between apps. In those cases, there's a lot to be said for extracting out a
"business logic" or "domain" layer to sit between your views and forms, and
your models.py which you can then keep as minimal as possible.

* With that said, https://forum.djangoproject.com/t/where-to-put-business-logic-in-django/282/7[word in the Django community] is that people find that "fat
models" runs into scalability problems of its own, particularly around
managing interdependencies between apps. In those cases, there's a lot
to be said for extracting out a "business logic" or "domain" layer to sit
between your views and forms, and your models.py which you can then keep
as minimal as possible.
=== Steps along the way

Supposing you're working on a Django project which you're not sure is going
to get complex enough to warrant the patterns we recommend, but you still
want to put a few steps in place to make your life easier, both in the medium
term, and if you want to migrate to some of our patterns later?

* One piece of advice we've heard is to put a __logic.py__ into every Django app,
from day one. This gives you a place to put business logic, and to keep your
forms, views and models free of business logic. It can become a stepping stone
for moving to a fully decoupled domain model and/or service layer later.

* A business logic layer might start out working with Django model objects,
and only later become fully decoupled from the framework and work on
plain Python data structures.

* Service Layer might be of interest if you're seeing a lot of duplication in
your views.py. It can be a good way of thinking about your use cases,
separately from your web endpoints.

* For the read-side, you can get some of the benefits of CQS by putting reads
into one place, avoiding ORM calls sprinkled all over the place

* When separating out modules for reads and modules for domain logic, it
may be worth decoupling yourself from the django apps hierarchy. Business
concerns will cut across them.


NOTE: We'd like to give a shout out to David Seddon and Ashia Zawaduk for
talking through some of the ideas in this chapter. They did their best to
stop us from saying anything really stupid about a topic we don't really
have enough personal experience of, but they may have failed.

For more thoughts and actual lived experience dealing with existing applications, read
on to <<epilogue_1_how_to_get_there_from_here>>.


////
conversation with ashia
-> i like separating a pure domain
why? when you add an api. if it's in the view, or in a form, it's hard to get out
ok so logic in the models? yes, but then you see increasing interconnectedness
between apps, they get tightly coupled
it's hard to find where the logic is. it would be nice to have it all in one place!
mentioned David W. and his layers. d is strict, a more relaxed. modelforms
can be useful!
-> could get a long way from just telling people to add a logic.py in every app
we came up with "handlers". it was great. you could see all the steps
CQRS chat
- is all business logic really on the write side? what about "who is allowed
to see what?"
short-term django advice:
- use model as minimal data layer
- add a domain layer between views and model that does all the business logic
- build a fetchers module for read-only queries, keep filtering and perms logic in here
- decouple yourself from django apps and models.
- at the beginning, you can maybe pass django model objects down from
views/forms to domain layer. later you can deconstruct (or never construct)
them, and reconstruct them at the data layer
////
For more thoughts and actual lived experience dealing with existing
applications, read on to <<epilogue_1_how_to_get_there_from_here>>.

0 comments on commit e1beca9

Please sign in to comment.