Hexagonal Design - Maarten van Schaik

  • Published on
    29-May-2015

  • View
    212

  • Download
    2

DESCRIPTION

Presentatie gegeven tijdens Amsterdam Python Meetup Group

Transcript

1. Hexagonal Design in Django Maarten van Schaik 2. Django Rapid application development ./manage.py startapp polls Define some models, forms, views and were 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 (APIs) using well- defined protocols Components have high cohesion Components are loosely coupled 6. Connected vs Modular 7. Ports and Adapters 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 9. Typical Django app __init__.py admin.py forms.py models.py tests.py urls.py views.py 10. App Uh oh Where is my application? ?? Models Views Forms 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, dont ask Views should render from immutable values So no vote.save() in views.py! 13. Example Poll def vote(request, poll_id): p = get_object_or_404(Poll, pk=poll_id) try: selected_choice = p.choice_set.get( pk=request.POST*choice+) except (KeyError, Choice.DoesNotExist): return render(request, , ,error: -) else: selected_choice.votes += 1 selected_choice.save() return HttpResponseRedirect() 14. Example Poll def vote(request, poll_id): p = get_object_or_404(Poll, pk=poll_id) try: selected_choice = p.choice_set.get( pk=request.POST*choice+) except (KeyError, Choice.DoesNotExist): return render(request, , ,error: -) else: selected_choice.votes += 1 selected_choice.save() return HttpResponseRedirect() Mutating data! 15. Example Poll (2) def vote(request, poll_id): try: poll_engine.register_vote(poll_id, request.POST*choice+) except Exception as e: return render(request, , ,error: -) else: return HttpResponseRedirect() 16. 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() 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 21. Thats it 22. Thanks and references Matt Wynne: Hexagonal Rails Kent Beck: To Design or Not To Design? Alistair Cockburn: Hexagonal Architecture Django tutorial