Slide 1

Slide 1 text

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

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 Z 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

The Clean Architecture A layered approach for a more civilized age

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

@blueprint.route('/items', methods=['GET']) def items(): pass In 2019 we are probably talking about a web application

Slide 16

Slide 16 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 17

Slide 17 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 18

Slide 18 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 19

Slide 19 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 20

Slide 20 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 21

Slide 21 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 22

Slide 22 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 23

Slide 23 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 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) return Response( json.dumps(result), mimetype='application/json', status=200) The web framework converts entities and simple structures into HTTP responses

Slide 25

Slide 25 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 26

Slide 26 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 27

Slide 27 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 28

Slide 28 text

HOW CAN WE USE THE CLEAN ARCHITECTURE WITH DJANGO?

Slide 29

Slide 29 text

The Clean Architecture The Django Architecture

Slide 30

Slide 30 text

The Clean Architecture The Django Architecture

Slide 31

Slide 31 text

The Clean Architecture The Django Architecture

Slide 32

Slide 32 text

The Clean Architecture The Django Architecture

Slide 33

Slide 33 text

The Clean Architecture The Django Architecture

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

IT DEPENDS

Slide 36

Slide 36 text

LOAD BALANCERS TO THE RESCUE

Slide 37

Slide 37 text

Is it possible to migrate an existing system?

Slide 38

Slide 38 text

Is this the definitive architecture?

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

Harry Percival, Bob Gregory github.com/python-leap/book Pythonic Application Architecture Patterns (working title)

Slide 41

Slide 41 text

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