KEMBAR78
Clean Architecture Applications in Python | PDF
Clean Architecture
Building Clean Apps in Python
Subhash Bhushan
Team8 Solutions, LLC
1
2 . 1
Technical Debt
Dependencies slow you down
2 . 2
3 . 1
Clean Architecture
Reduce Technical Debt by Reducing Dependencies
Thou shalt only
look inward!
3 . 2
Example
1. Check Balance in Account 1
2. Initiate Transaction
3. Debit Account 1
4. Credit Account 2
5. Record Transfer
6. Commit Transaction
An Implementation of Account to Account Transfer
4 . 1
Entities
from datetime import datetime
class Account:
# Look Ma, No DB!
def __init__(self, name, balance):
self.name = name
self.balance = balance
def validate(self):
pass
def has_sufficient_balance(self, amount):
return self.balance >= amount
def debit(self, amount):
if self.has_sufficient_balance(amount):
self.balance -= amount
def credit(self, amount):
self.balance += amount
class Transfer:
def __init__(self, from_account: Account, to_account: Account, amount: float):
self.from_account = from_account
self.to_account = to_account
self.amount = amount
self.transaction_date = datetime.now()
4 . 2
UseCase
from core.entities import Transfer
from db.repository import TransferRepository
class TransferUseCase:
def __init__(self, repository: TransferRepository): # Dependency... Injected.
self.repository = repository
def create(self, transfer: Transfer):
if transfer.from_account.validate() 
and transfer.to_account.validate() 
and transfer.from_account.has_sufficient_balance(transfer.amount)
and self._validate_transfer_request(transfer):
with self.repository.atomic():
transfer = self.repository.save(transfer)
return transfer
def _validate_transfer_request(self, transfer: Transfer):
pass
4 . 3
Data Adapters
from typing import NamedTuple # Don't Type Hints make your code look great?
from core.entities import Transfer
from core.usecase import TransferUseCase
from db.repository import TransferRepository
class AccountData(NamedTuple):
name: str
balance: float
class TransferData(NamedTuple):
from_account: AccountData
to_account: AccountData
amount: float
transaction_date: str
4 . 4
Interface Adapters
from typing import NamedTuple
from core.entities import Transfer
from core.usecase import TransferUseCase
from db.repository import TransferRepository
class TransferAdapter:
def __init__(self, repository: TransferRepository): # Some more... Injection.
self.usecase = TransferUseCase(repository)
def create(self, transfer_data: TransferData) -> TransferData:
transfer = self._data_to_transfer(transfer_data)
transfer = self.usecase.create(transfer) # Warning: No Exceptions Please!
return self._transfer_to_data(transfer)
@classmethod
def _transfer_to_data(cls, transfer: Transfer) -> TransferData:
pass
@classmethod
def _data_to_transfer(cls, transfer_data: TransferData) -> Transfer:
pass
4 . 5
Repository
from typing import ContextManager
from core.entities import Transfer
class TransferRepository:
def save(self, transfer: Transfer) -> Transfer:
# And Finally...
# Persist Transfer AND Accounts
raise NotImplementedError()
def atomic(self) -> ContextManager:
raise NotImplementedError()
4 . 6
Views
import json
from flask import request
from flask_restful import Resource, Api
from core.adapters import TransferAdapter
from db.repository import TransferRepository
class TransferResource(Resource):
def __init__(self, *args, **kwargs):
self.super().__init__(*args, **kwargs)
self.adapter = TransferAdapter(TransferRepository()) # Inject... Dependency
@app.route('/api/v1.0/transfer', methods=['POST'])
def post(self):
transfer_data = self.adapter.create(request.form['data'])
return transfer_data, 201 # Again, No Exceptions, Please!
4 . 7
Cons
Everyone needs to respect rules
Difficult to leverage frameworks
Cannot take advantage of Active Record pattern
Can result in Significant Boilerplate
Higher Complexity
There ain't no such thing as a Free Lunch
5
The Principle of
Last Responsible Moment
A good architecture allows you to defer critical decisions
6 . 1
Resources
The Clean Architecture -
Robert C. Martin's Clean Architecture -
Alistair Cockburn -
Clean Architecture Python Apps -
Clean Architecture is Screaming -
Github Repo -
8thlight.com
Amazon.com
Hexagonal Architecture
@haxoza
DZone
Clean Transfer
All Icons are courtesy of the good folks at The Noun Project
6 . 2
Thank you!
Questions?
subhash@team8solutions.com
6 . 3

Clean Architecture Applications in Python

  • 1.
    Clean Architecture Building CleanApps in Python Subhash Bhushan Team8 Solutions, LLC 1
  • 2.
  • 3.
  • 4.
  • 5.
    Clean Architecture Reduce TechnicalDebt by Reducing Dependencies Thou shalt only look inward! 3 . 2
  • 6.
    Example 1. Check Balancein Account 1 2. Initiate Transaction 3. Debit Account 1 4. Credit Account 2 5. Record Transfer 6. Commit Transaction An Implementation of Account to Account Transfer 4 . 1
  • 7.
    Entities from datetime importdatetime class Account: # Look Ma, No DB! def __init__(self, name, balance): self.name = name self.balance = balance def validate(self): pass def has_sufficient_balance(self, amount): return self.balance >= amount def debit(self, amount): if self.has_sufficient_balance(amount): self.balance -= amount def credit(self, amount): self.balance += amount class Transfer: def __init__(self, from_account: Account, to_account: Account, amount: float): self.from_account = from_account self.to_account = to_account self.amount = amount self.transaction_date = datetime.now() 4 . 2
  • 8.
    UseCase from core.entities importTransfer from db.repository import TransferRepository class TransferUseCase: def __init__(self, repository: TransferRepository): # Dependency... Injected. self.repository = repository def create(self, transfer: Transfer): if transfer.from_account.validate() and transfer.to_account.validate() and transfer.from_account.has_sufficient_balance(transfer.amount) and self._validate_transfer_request(transfer): with self.repository.atomic(): transfer = self.repository.save(transfer) return transfer def _validate_transfer_request(self, transfer: Transfer): pass 4 . 3
  • 9.
    Data Adapters from typingimport NamedTuple # Don't Type Hints make your code look great? from core.entities import Transfer from core.usecase import TransferUseCase from db.repository import TransferRepository class AccountData(NamedTuple): name: str balance: float class TransferData(NamedTuple): from_account: AccountData to_account: AccountData amount: float transaction_date: str 4 . 4
  • 10.
    Interface Adapters from typingimport NamedTuple from core.entities import Transfer from core.usecase import TransferUseCase from db.repository import TransferRepository class TransferAdapter: def __init__(self, repository: TransferRepository): # Some more... Injection. self.usecase = TransferUseCase(repository) def create(self, transfer_data: TransferData) -> TransferData: transfer = self._data_to_transfer(transfer_data) transfer = self.usecase.create(transfer) # Warning: No Exceptions Please! return self._transfer_to_data(transfer) @classmethod def _transfer_to_data(cls, transfer: Transfer) -> TransferData: pass @classmethod def _data_to_transfer(cls, transfer_data: TransferData) -> Transfer: pass 4 . 5
  • 11.
    Repository from typing importContextManager from core.entities import Transfer class TransferRepository: def save(self, transfer: Transfer) -> Transfer: # And Finally... # Persist Transfer AND Accounts raise NotImplementedError() def atomic(self) -> ContextManager: raise NotImplementedError() 4 . 6
  • 12.
    Views import json from flaskimport request from flask_restful import Resource, Api from core.adapters import TransferAdapter from db.repository import TransferRepository class TransferResource(Resource): def __init__(self, *args, **kwargs): self.super().__init__(*args, **kwargs) self.adapter = TransferAdapter(TransferRepository()) # Inject... Dependency @app.route('/api/v1.0/transfer', methods=['POST']) def post(self): transfer_data = self.adapter.create(request.form['data']) return transfer_data, 201 # Again, No Exceptions, Please! 4 . 7
  • 13.
    Cons Everyone needs torespect rules Difficult to leverage frameworks Cannot take advantage of Active Record pattern Can result in Significant Boilerplate Higher Complexity There ain't no such thing as a Free Lunch 5
  • 14.
    The Principle of LastResponsible Moment A good architecture allows you to defer critical decisions 6 . 1
  • 15.
    Resources The Clean Architecture- Robert C. Martin's Clean Architecture - Alistair Cockburn - Clean Architecture Python Apps - Clean Architecture is Screaming - Github Repo - 8thlight.com Amazon.com Hexagonal Architecture @haxoza DZone Clean Transfer All Icons are courtesy of the good folks at The Noun Project 6 . 2
  • 16.