Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Clean Architectures in Python - Django edition

Clean Architectures in Python - Django edition

Architectural considerations are often overlooked by developers or completely delegated to a framework. We should start once again discussing how applications are structured, how components are connected and how to lower coupling between different parts of a system, to avoid creating software that cannot easily be maintained or changed.

The “clean architecture” model predates Robert Martin, who recently brought it back to the attention of the community, and is a way of structuring applications that leverages layers separation and internal APIs to achieve a very tidy, fully-tested, and loosely coupled system. The talk introduces the main ideas of the architecture, showing how the layers can be implemented in Python, following the content of the book “Clean Architectures in Python” edited by Leanpub. The book recently reached 8,000 downloads and many readers found it useful to start learning how to test software and how to structure an application without relying entirely on the framework.

This version of the presentation includes considerations on Django and how Django and the Clean Architecture can be used together.

Leonardo Giordani

September 09, 2019
Tweet

More Decks by Leonardo Giordani

Other Decks in Programming

Transcript

  1. Clean Architectures in Python A tale of durability, utility, and

    beauty Django edition
  2. “Who wrote this code?” L E O N A R

    D O G I O R D A N I S O F T W A R E D E V E L O P E R A N D B L O G G E R W W W. T H E D I G I T A L C AT O N L I N E . C O M @TW_LGIORDANI - @THEDIGICAT
  3. WHAT IS THE DEFINITION OF ARCHITECTURE?

  4. F I R M I T A S , U

    T I L I T A S , V E N U S T A S Vitruvius, De architectura
  5. D U R A B I L I T Y,

    U T I L I T Y, B E A U T Y Vitruvius, De architectura
  6. T H E A R T A N D S

    C I E N C E I N W H I C H T H E C O M P O N E N T S O F A C O M P U T E R S Y S T E M A R E O R G A N I Z E D A N D I N T E G R AT E D
  7. D O W E N E E D A R

    C H I T E C T U R E ?
  8. Ivar Jacobson (1992) Object Oriented Software Engineering: A Use-Case Driven

    Approach E. Gamma, R. Helm, R. Johnson, J. Vlissides (1994) Design Patterns Robert Martin (2000) Design Principles and Design Patterns Eric Evans (2003) Domain-Driven Design: Tackling Complexity in the Heart of Software H. Hohpe, B. Woolf (2003) Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions
  9. What is the meaning of clean?

  10. You know where things are, why components are there, what

    something is.
  11. The Clean Architecture A layered approach for a more civilized

    age
  12. The golden rule Talk inward with simple structures, talk outwards

    through interfaces.
  13. class Item: def __init__(self, code, price): self.code = code self.price

    = price Entities: simple models
  14. Use case: retrieve a list of items use_case = uc.ItemsListUseCase()

    use_case.execute()
  15. @blueprint.route('/items', methods=['GET']) def items(): pass In 2019 we are probably

    talking about a web application
  16. @blueprint.route('/items', methods=['GET']) def items(): use_case = uc.ItemsListUseCase() use_case.execute(request.args) Incoming HTTP

    requests become a call and simple structures
  17. @blueprint.route('/items', methods=['GET']) def items(): use_case = uc.ItemsListUseCase() use_case.execute(request.args) The use

    case extracts data from a repository, which can be any source of data
  18. @blueprint.route('/items', methods=['GET']) def items(): use_case = uc.ItemsListUseCase() use_case.execute(request.args) And the

    repository can be accessed through an interface
  19. @blueprint.route('/items', methods=['GET']) def items(): repo = PostgresRepo(CONNECTION_STRING) use_case = uc.ItemsListUseCase(repo)

    use_case.execute(request.args) The use case receives the repository interface as an argument of the call
  20. class ItemsListUseCase: def __init__(self, repo): self.repo = repo def execute(self,

    params): # BUSINESS LOGIC HERE result = self.repo.list(params) # BUSINESS LOGIC HERE return result The use case queries the repository interface with simple structures
  21. class PostgresRepo: def __init__(self, CONNECTION_STRING): self.ng = create_engine( CONNECTION_STRING) Base.metadata.bind

    = self.ng def list(self, filters): DBSession = sessionmaker(bind=self.ng) session = DBSession() query = ... The database interface and the database exchange data in a specific language
  22. The database interface translates the specific language into simple structures

    and entities class PostgresRepo: def __init__(self, CONNECTION_STRING): self.ng = create_engine( CONNECTION_STRING) Base.metadata.bind = self.ng def _create_items(self, results): return [Item(code=q.code, price=q.price) for q in results] def list(self, filters): DBSession = sessionmaker(bind=self.ng) session = DBSession() query = ... return self._create_items(query.all())
  23. @blueprint.route('/items', methods=['GET']) def items(): repo = PostgresRepo(CONNECTION_STRING) use_case = uc.ItemsListUseCase(repo)

    result = use_case.execute(request.args) The use case returns the result of the business logic: entities and simple structures
  24. @blueprint.route('/items', methods=['GET']) def items(): repo = PostgresRepo(CONNECTION_STRING) use_case = uc.ItemsListUseCase(repo)

    result = use_case.execute(request.args) return Response( json.dumps(result), mimetype='application/json', status=200) The web framework converts entities and simple structures into HTTP responses
  25. class ItemsListUseCase: def __init__(self, repo): self.repo = repo def execute(self,

    params): # BUSINESS LOGIC HERE result = self.repo.list(params) # BUSINESS LOGIC HERE return result Testing the use case
  26. @blueprint.route('/items', methods=['GET']) def items(): repo = PostgresRepo(CONNECTION_STRING) use_case = uc.ItemsListUseCase(repo)

    result = use_case.execute(request.args) return Response( json.dumps(result), mimetype='application/json', status=200) Testing the HTTP endpoint
  27. Testing the repository interface: integration test class PostgresRepo: def __init__(self,

    CONNECTION_STRING): self.ng = create_engine( CONNECTION_STRING) Base.metadata.bind = self.ng def _create_items(self, results): return [Item(code=q.code, price=q.price) for q in results] def list(self, filters): DBSession = sessionmaker(bind=self.ng) session = DBSession() query = ... return self._create_items(query.all())
  28. HOW CAN WE USE THE CLEAN ARCHITECTURE WITH DJANGO?

  29. The Clean Architecture The Django Architecture

  30. The Clean Architecture The Django Architecture

  31. The Clean Architecture The Django Architecture

  32. The Clean Architecture The Django Architecture

  33. The Clean Architecture The Django Architecture

  34. None
  35. IT DEPENDS

  36. LOAD BALANCERS TO THE RESCUE

  37. Is it possible to migrate an existing system?

  38. Is this the definitive architecture?

  39. Clean Architectures in Python A practical approach to better software

    design bit.ly/getpycabook Leonardo Giordani
  40. Harry Percival, Bob Gregory github.com/python-leap/book Pythonic Application Architecture Patterns (working

    title)
  41. Thank you! @tw_lgiordani - @thedigicat - bit.ly/getpycabook https://speakerdeck.com/lgiordani