Slide 1

Slide 1 text

Clean Architectures in Python A tale of durability, utility, and beauty

Slide 2

Slide 2 text

“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

Slide 3

Slide 3 text

WHAT IS THE DEFINITION OF ARCHITECTURE?

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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 S E D A N D I N T E G R AT E D

Slide 7

Slide 7 text

D O W E N E E D A R C H I T E C T U R E ?

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

What is the meaning of clean?

Slide 10

Slide 10 text

You know where things are, why components are there, what something is.

Slide 11

Slide 11 text

Clean Architectures in Python A practical approach to better software design bit.ly/getpycabook Leonardo Giordani

Slide 12

Slide 12 text

The Clean Architecture A layered approach for a more civilized age

Slide 13

Slide 13 text

The golden rule Talk inward with simple structures, talk outwards through interfaces.

Slide 14

Slide 14 text

class Item: def __init__(self, code, price): self.code = code self.price = price Entities: simple models

Slide 15

Slide 15 text

Use case: retrieve a list of items use_case = uc.ItemsListUseCase() use_case.execute()

Slide 16

Slide 16 text

@blueprint.route('/items', methods=['GET']) def items(): pass We want to build a web application

Slide 17

Slide 17 text

@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

Slide 18

Slide 18 text

@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

Slide 19

Slide 19 text

@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

Slide 20

Slide 20 text

@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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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())

Slide 24

Slide 24 text

@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

Slide 25

Slide 25 text

@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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

@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

Slide 28

Slide 28 text

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())

Slide 29

Slide 29 text

Is it possible to migrate an existing system?

Slide 30

Slide 30 text

Is this the definitive architecture?

Slide 31

Slide 31 text

Clean Architectures in Python A practical approach to better software design bit.ly/getpycabook Leonardo Giordani

Slide 32

Slide 32 text

Harry Percival, Bob Gregory www.cosmicpython.com Architecture Patterns with Python

Slide 33

Slide 33 text

Thank you! @tw_lgiordani - @thedigicat bit.ly/getpycabook - thedigitalcatonline.com