This document discusses refactoring Django applications using a hexagonal design pattern to improve modularity, loose coupling, and testability. It describes how a typical Django application can become coupled to the framework over time as new features are added. The hexagonal design pattern advocates separating the core domain model from the framework using adapters, ports, and defining interfaces. This allows the core model to remain framework agnostic. The document provides an example of refactoring a poll application vote handling to follow these principles by moving data access and mutating logic out of the views and into separate, testable services.
2. Django
• Rapid application development
• ./manage.py startapp polls
• Define some models, forms, views and we’re
in business!
3. Applications live and grow
• More features are added:
– API access
– Reporting
– Command Line Interface
– Integration with other applications
– Who knows what else…
4. Connected Design
• Components can access all other components
• When you need to access a piece of data or a
piece of logic, just import and use it
• Development is fast
5. Modular design
• Access to other components goes through
well-defined interfaces (API’s) using well-
defined protocols
• Components have high cohesion
• Components are loosely
coupled
8. Ports and Adapters
• Specific adapter for each use of the
application, e.g. web view, command line,
message queue, etc.
• Each adapter connects to a port of the
application
• Mock adapters and test harnesses facilitate
testing
• Testing becomes easier and faster
11. Refactoring Django apps
• Rules
– Core domain model cannot depend on Django
– Tell objects, ask values
12. Implications
• Core model cannot depend on framework
– Core model cannot derive from models.Model
– Communication with Django goes through
adapters
• Tell, don’t ask
– Views should render from immutable values
– So no vote.save() in views.py!
17. Example – Poll (2)
## Poll engine
def register_vote(poll_id, choice_id):
p = Poll.objects.get(pk=poll_id)
selected_choice = p.choice_set.get(pk=choice_id)
selected_choice.votes += 1
selected_choice.save()
Dependency on Django models
18. Example – Poll (3)
## Poll engine
def register_vote(poll_id, choice_id):
if not poll_repository.choice_exists(poll_id, choice_id):
raise PollException(…)
poll_repository.increment_vote_count(choice_id)
19. Example – Poll (3)
## Django model adapter
def choice_exists(poll_id, choice_id):
return Choice.objects.filter(
poll_id=poll_id, pk=choice_id).exists()
def increment_vote_count(choice_id)
Choice.objects.filter(pk=choice_id).update(
votes=F(‘votes’)+1)
20. Conclusions
• Hexagonal design will help keep speed of
adding new features constant
• Encourages modularity and encapsulation
• Encourages clean and well-organized
applications
• Tests become faster when using plain objects
and data
• Django models are not that useful without
coupling with them
22. Thanks and references
• Matt Wynne: Hexagonal Rails
• Kent Beck: To Design or Not To Design?
• Alistair Cockburn: Hexagonal Architecture
• Django tutorial