Clean Architectures in Python

Clean Architectures in Python

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.

C7153ea32bae30ac5d06de504be1d7a4?s=128

Leonardo Giordani

June 16, 2019
Tweet

Transcript

  1. 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
  2. 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
  3. 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
  4. 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
  5. 7.

    D O W E N E E D A R

    C H I T E C T U R E ?
  6. 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
  7. 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
  8. 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
  9. 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
  10. 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())
  11. 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
  12. 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
  13. 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
  14. 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
  15. 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())
  16. 30.

    Clean Architectures in Python A practical approach to better software

    design bit.ly/getpycabook Leonardo Giordani