SlideShare a Scribd company logo
1 of 37
Download to read offline
Domain modelling with
types using mypy
Jean Carlo Machado
Motivation / Idea
● I’m a fan of functional programming and Domain Driven Design
● used Haskell, Elm, and saw something valuable there
● But never managed to convert those ideas to business reality properly
● Hard to find material about it, rather you are a super expert or knows
nothing at all
Motivation / Idea
Until...
Motivation / Idea
● Mypy bring this concepts to something more palpable than haskell
● The concepts are replicable to other languages, open-api, etc
● Fits as a glove with event based distributed systems
● Assumes some DDD and FP knowledge
Goal
Leave you inspired to seek more about the topic
What is a type?
Set of possible value that can be used as inputs or outputs of a function
What is a type system?
Formalize and enforce the otherwise implicit categories (types) the programmer uses.
The main purpose of a type system is to reduce possibilities for bugs in computer
programs.
Type systems have other purposes as well, such as expressing business rules, enabling
certain compiler optimizations, providing a form of documentation, etc.
What is mypy?
Mypy is a static type checker for Python 3 and Python 2.7.
A tool that implements pep 484, types are optional
No runtime validation/overhead
Used by Google and Dropbox
Facebook have their own versions of it
Building blocks of the type-system algebra
Using my startup idea to kill goodreads as example (okreads).
Simple values - New type
People don’t think in term of
integers and string. They talk
OrderId, customer name, etc
Isbn10 = NewType('Isbn10', str)
Isbn5 = NewType('Isbn5', str)
Simple values - New type
def download_book_by_isbn(isbn: Isbn10):
...
isbn_string = "978-3-16-148410-0"
isbn_type = Isbn10(isbn_string)
download_book_by_isbn(isbn_type)
download_book_by_isbn(isbn_string)
mypy test.py
test.py:14: error: Argument 1 to "download_book_by_isbn" has
incompatible type "str"; expected "Isbn10"
Found 1 error in 1 file (checked 1 source file)
Combinations of values AND
IsbnInfo = NamedTuple('IsbnInfo', [('isbn5', Optional[Isbn5]),
('isbn10', Optional[Isbn10])])
Modelling choices OR
Attribute undefined
errors are one of the
most common in
programming
Optional = Union[T, None]
Modelling choices OR
Also good for modeling
different states in the
domain. Types can also
represent certainty.
AIsbn = Union[Isbn5, Isbn10]
Modelling choices OR
Also good for modeling
different states in the
domain
UnvalidatedBookData = NamedTuple('UnvalidatedBookData', [('author_data', Any), ('title', str)])
class ValidatedBookData:
def __init__(self, data: UnvalidatedBookData):
if len(data.title) < 3:
raise Exception('Title should be bigger than 3 chars')
self.title = data.title
self.author_data = data.author_data
PersistedBookData = NewType('PersistedBookData', 'ValidatedBookData')
BookState = Union[UnvalidatedBookData, ValidatedBookData, PersistedBookData]
Type Functions
LoadOpenLibraryBookReference = Callable[[ExistingFile, Optional[LinesToLoadLimit]], BookReferences]
Classes to do what the type-system cannot
class ExistingFile:
def __init__(self, location: str):
if not os.path.exists(location):
raise Exception("File does not exist")
self.location = location
Generics
CommandData = TypeVar('CommandData')
class Command(Generic[CommandData]):
def __init__(self, data: CommandData, issuer: CommandIssuer):
self.data = data
self.issuer = issuer
List is a wrapper of something generic.
Putting it all together
Rethinking the paradigm of modelling software
We are biased towards OOP and RDBSs
Database modeling
OOP modeling
Workflow modeling
Workflows are composed by sub workflows
Workflows talking
Avoid flag types
Status = Literal["unvalidated", "validated"]
Book = NamedTuple('Book', [('author_data', Any), ('title', str), ('status', Status)])
def persist(book: Book):
if book.status != 'validated':
raise Exception("Cannot save unvalidated")
#save here
Every business subworkflow returns a new type
UnvalidatedBookData = NamedTuple('UnvalidatedBookData', [('author_data', Any), ('title', str)])
ValidatedBookData = NewType('ValidatedBookData', UnvalidatedBookData)
PersistedBookData = NewType('PersistedBookData', ValidatedBookData)
def persist(book: ValidatedBookData) -> PersistedBookData:
...
Avoid corrupting the model to keep fewer entities
Review = NamedTuple('Review', [('title', Title) ,('author', Title)])
## add activity approach 1
Review = NamedTuple('Review', [('title', Title) ,('author', Author), ('activity', Optional[Activity]) ])
# add activity approach 2
ReviewWithActivity = NamedTuple('ReviewWithActivity', [('Review', Review), ('activity', Activity) ])
# approach 3
QualifiedReview = NamedTuple(QualifiedReview, [...])
Workflow - dependencies
LoadOpenLibraryBookReference = Callable[['ExistingFile', Optional['LinesToLoadLimit']], BookReferences]
LoadOpenLibraryBookData = Callable[[OpenLibraryBookReference], 'UnvalidatedBookData']
PersistBook = Callable[['ValidatedBookData'], PersistedBookData]
class Workflow:
def __init__(self, book_reference_loader: LoadOpenLibraryBookReference, book_data_loader: LoadOpenLibraryBookData,
persist_book: PersistBook):
self.book_reference_loader = book_reference_loader
self.book_data_loader = book_data_loader
self.persist_book = persist_book
A OOP class constructor is a
higher order function that
injects types and returns an
aggregated type(the object)
with the state injected and
the set of callables it contains
Complete Workflow
LoadOpenLibraryBookReference = Callable[['ExistingFile', Optional['LinesToLoadLimit']], BookReferences]
LoadOpenLibraryBookData = Callable[[OpenLibraryBookReference], 'UnvalidatedBookData']
PersistBook = Callable[['ValidatedBookData'], PersistedBookData]
class Workflow:
def run(self, cmd: SyncOpenLibraryCmd) -> BookPersistedEvents:
limit = None
if cmd.limit:
limit = LinesToLoadLimit(cmd.limit)
for reference in self.book_reference_loader(ExistingFile(cmd.filename), limit):
unvalidated_book_data = self.book_data_loader(reference)
validated_book_data = ValidatedBookData(unvalidated_book_data)
persisted_book = persist_book(validated_book_data)
yield BookPersistedEvent(persisted_book_data=persisted_book)
Errors from experience
Business types
should be valid
all the time
(owning its
instantiation)
def activity_from_dict(data: dict) -> Activity:
image = EntitiesFactories.image_from_dict(data['image']) if 'image' in data else None
labels: List[Label] = []
if data.get('labels'):
labels = list(map(EntitiesFactories.label_from_dict, data['labels']))
return Activity(activityId=data['activityId'],
title=data.get('title', ''),
experienceId=data.get('experienceId', None),
isAvailable=data.get('isAvailable', True),
description=data.get('description', ''),
price=price_from_dict(data['price']),
rating=rating_from_dict(data.get('rating', {})),
duration=duration_from_dict(data.get('duration', {})),
isCertified=data.get('isCertified', False),
isOnline=data.get('isOnline', True),
labels=labels,
isOriginal=is_original(data['activityId']),
adpUrl=data.get('adpUrl', ''),
image=image,
type=data.get('type', ''),
attributes=data.get('attributes', []))
Use classes/objects for:
Workflows
Validation of integrity of value objects and aggregate roots
Equality checks, merging aggregate roots
Custom serialization
Limitations of mypy - no recursive types
Employee = NamedTuple('Employee', [('id', Id), ('manager', 'Employee')])
mypy tree.py
tree.py:4: error: Cannot resolve name "Employee" (possible cyclic definition)
Found 1 error in 1 file (checked 1 source file)
Limitations of mypy
no private constructors without hack
no inheritance of union type or aliasing
Key learnings
When types are cheap you can model all valid states of your business with it.
Every function that change something in the business should return a new type.
Make illegal states impossible by modelling all valid ones in the type-system
Strive to represent the system state by the type-system
Complements and alternatives of mypy
pyre - facebook
pytype - google
pyannotate - automatically annotate your functions (Dropbox)
monkeytype: generate runtime annotations for types (instagram)
Resources
● Podcast: Gradual Typing of Production Applications
● Example of mypy in GYG: Search service
● Our journey to type checking
● DDD at 10 years
● Okreads source applying this ideas
The end
Thanks for listening
Just like the builders of Europe’s great Gothic cathedrals we’ve been honing our craft to the limits of material and structure. There is
an unfinished Gothic cathedral in Beauvais, France, that stands witness to this deeply human struggle with limitations. It was intended
to beat all previous records of height and lightness, but it suffered a series of collapses. Ad hoc measures like iron rods and wooden
supports keep it from disintegrating, but obviously a lot of things went wrong. From a modern perspective, it’s a miracle that so many
Gothic structures had been successfully completed without the help of modern material science, computer modelling, finite element
analysis, and general math and physics. I hope future generations will be as admiring of the programming skills we’ve been displaying
in building complex operating systems, web servers, and the internet infrastructure. And, frankly, they should, because we’ve done all
this based on very flimsy theoretical foundations. We have to fix those foundations if we want to move forward. - Bartosz Milewski’s

More Related Content

What's hot

Présentation de ECMAScript 6
Présentation de ECMAScript 6Présentation de ECMAScript 6
Présentation de ECMAScript 6
Julien CROUZET
 

What's hot (20)

End to end Machine Learning using Kubeflow - Build, Train, Deploy and Manage
End to end Machine Learning using Kubeflow - Build, Train, Deploy and ManageEnd to end Machine Learning using Kubeflow - Build, Train, Deploy and Manage
End to end Machine Learning using Kubeflow - Build, Train, Deploy and Manage
 
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t...
Algebraic Data Types forData Oriented Programming - From Haskell and Scala t...Algebraic Data Types forData Oriented Programming - From Haskell and Scala t...
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t...
 
KFServing and Feast
KFServing and FeastKFServing and Feast
KFServing and Feast
 
Modern JS with ES6
Modern JS with ES6Modern JS with ES6
Modern JS with ES6
 
Functional Domain Modeling - The ZIO 2 Way
Functional Domain Modeling - The ZIO 2 WayFunctional Domain Modeling - The ZIO 2 Way
Functional Domain Modeling - The ZIO 2 Way
 
Concurrency With Go
Concurrency With GoConcurrency With Go
Concurrency With Go
 
HTML5 Form Validation
HTML5 Form ValidationHTML5 Form Validation
HTML5 Form Validation
 
Luft : 10억 데이터를 10초만에 쿼리하는 DB 개발기
Luft : 10억 데이터를 10초만에 쿼리하는 DB 개발기Luft : 10억 데이터를 10초만에 쿼리하는 DB 개발기
Luft : 10억 데이터를 10초만에 쿼리하는 DB 개발기
 
Switching from relational to the graph model
Switching from relational to the graph modelSwitching from relational to the graph model
Switching from relational to the graph model
 
Workshop 21: React Router
Workshop 21: React RouterWorkshop 21: React Router
Workshop 21: React Router
 
Présentation de ECMAScript 6
Présentation de ECMAScript 6Présentation de ECMAScript 6
Présentation de ECMAScript 6
 
Domain Modeling in a Functional World
Domain Modeling in a Functional WorldDomain Modeling in a Functional World
Domain Modeling in a Functional World
 
Angular overview
Angular overviewAngular overview
Angular overview
 
[MLOps KR 행사] MLOps 춘추 전국 시대 정리(210605)
[MLOps KR 행사] MLOps 춘추 전국 시대 정리(210605)[MLOps KR 행사] MLOps 춘추 전국 시대 정리(210605)
[MLOps KR 행사] MLOps 춘추 전국 시대 정리(210605)
 
Dapper performance
Dapper performanceDapper performance
Dapper performance
 
Node.js Express
Node.js  ExpressNode.js  Express
Node.js Express
 
Functional Design Patterns (DevTernity 2018)
Functional Design Patterns (DevTernity 2018)Functional Design Patterns (DevTernity 2018)
Functional Design Patterns (DevTernity 2018)
 
weather-data-processing-using-python
weather-data-processing-using-pythonweather-data-processing-using-python
weather-data-processing-using-python
 
Angular Pipes Workshop
Angular Pipes WorkshopAngular Pipes Workshop
Angular Pipes Workshop
 
JSP - Java Server Page
JSP - Java Server PageJSP - Java Server Page
JSP - Java Server Page
 

Similar to Domain Driven Design Made Functional with Python

ClassifyingIssuesFromSRTextAzureML
ClassifyingIssuesFromSRTextAzureMLClassifyingIssuesFromSRTextAzureML
ClassifyingIssuesFromSRTextAzureML
George Simov
 
Discovering User's Topics of Interest in Recommender Systems
Discovering User's Topics of Interest in Recommender SystemsDiscovering User's Topics of Interest in Recommender Systems
Discovering User's Topics of Interest in Recommender Systems
Gabriel Moreira
 
Python introduction
Python introductionPython introduction
Python introduction
Roger Xia
 
conceptsinobjectorientedprogramminglanguages-12659959597745-phpapp02.pdf
conceptsinobjectorientedprogramminglanguages-12659959597745-phpapp02.pdfconceptsinobjectorientedprogramminglanguages-12659959597745-phpapp02.pdf
conceptsinobjectorientedprogramminglanguages-12659959597745-phpapp02.pdf
SahajShrimal1
 

Similar to Domain Driven Design Made Functional with Python (20)

pythontraining-201jn026043638.pptx
pythontraining-201jn026043638.pptxpythontraining-201jn026043638.pptx
pythontraining-201jn026043638.pptx
 
C# Summer course - Lecture 1
C# Summer course - Lecture 1C# Summer course - Lecture 1
C# Summer course - Lecture 1
 
Functions, List and String methods
Functions, List and String methodsFunctions, List and String methods
Functions, List and String methods
 
Python training
Python trainingPython training
Python training
 
Dynamic Python
Dynamic PythonDynamic Python
Dynamic Python
 
Python for dummies
Python for dummiesPython for dummies
Python for dummies
 
Oop java
Oop javaOop java
Oop java
 
Property Based Testing in PHP
Property Based Testing in PHPProperty Based Testing in PHP
Property Based Testing in PHP
 
A Gentle Introduction to Coding ... with Python
A Gentle Introduction to Coding ... with PythonA Gentle Introduction to Coding ... with Python
A Gentle Introduction to Coding ... with Python
 
ClassifyingIssuesFromSRTextAzureML
ClassifyingIssuesFromSRTextAzureMLClassifyingIssuesFromSRTextAzureML
ClassifyingIssuesFromSRTextAzureML
 
PRESENTATION ON PYTHON.pptx
PRESENTATION ON PYTHON.pptxPRESENTATION ON PYTHON.pptx
PRESENTATION ON PYTHON.pptx
 
Discovering User's Topics of Interest in Recommender Systems
Discovering User's Topics of Interest in Recommender SystemsDiscovering User's Topics of Interest in Recommender Systems
Discovering User's Topics of Interest in Recommender Systems
 
biopython, doctest and makefiles
biopython, doctest and makefilesbiopython, doctest and makefiles
biopython, doctest and makefiles
 
Drupal 8: A story of growing up and getting off the island
Drupal 8: A story of growing up and getting off the islandDrupal 8: A story of growing up and getting off the island
Drupal 8: A story of growing up and getting off the island
 
Modules of the twenties
Modules of the twentiesModules of the twenties
Modules of the twenties
 
python note.pdf
python note.pdfpython note.pdf
python note.pdf
 
Python introduction
Python introductionPython introduction
Python introduction
 
Concepts In Object Oriented Programming Languages
Concepts In Object Oriented Programming LanguagesConcepts In Object Oriented Programming Languages
Concepts In Object Oriented Programming Languages
 
First Steps in Python Programming
First Steps in Python ProgrammingFirst Steps in Python Programming
First Steps in Python Programming
 
conceptsinobjectorientedprogramminglanguages-12659959597745-phpapp02.pdf
conceptsinobjectorientedprogramminglanguages-12659959597745-phpapp02.pdfconceptsinobjectorientedprogramminglanguages-12659959597745-phpapp02.pdf
conceptsinobjectorientedprogramminglanguages-12659959597745-phpapp02.pdf
 

More from Jean Carlo Machado (11)

Python clean code for data producs
Python clean code for data producsPython clean code for data producs
Python clean code for data producs
 
Search microservice
Search microserviceSearch microservice
Search microservice
 
Git avançado
Git avançadoGit avançado
Git avançado
 
Functional php
Functional phpFunctional php
Functional php
 
Why functional programming matters
Why functional programming mattersWhy functional programming matters
Why functional programming matters
 
Clean code v3
Clean code v3Clean code v3
Clean code v3
 
Clean Code V2
Clean Code V2Clean Code V2
Clean Code V2
 
Review articles bio inspired algorithms
Review articles bio inspired algorithmsReview articles bio inspired algorithms
Review articles bio inspired algorithms
 
Introduction to Rust
Introduction to RustIntroduction to Rust
Introduction to Rust
 
Limitações do HTML no Desenvolvimento de Jogos Multiplataforma
Limitações do HTML no Desenvolvimento de Jogos MultiplataformaLimitações do HTML no Desenvolvimento de Jogos Multiplataforma
Limitações do HTML no Desenvolvimento de Jogos Multiplataforma
 
Clean code
Clean codeClean code
Clean code
 

Recently uploaded

CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICECHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
9953056974 Low Rate Call Girls In Saket, Delhi NCR
 
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️
anilsa9823
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service provider
mohitmore19
 
CALL ON ➥8923113531 🔝Call Girls Badshah Nagar Lucknow best Female service
CALL ON ➥8923113531 🔝Call Girls Badshah Nagar Lucknow best Female serviceCALL ON ➥8923113531 🔝Call Girls Badshah Nagar Lucknow best Female service
CALL ON ➥8923113531 🔝Call Girls Badshah Nagar Lucknow best Female service
anilsa9823
 

Recently uploaded (20)

Hand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxHand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptx
 
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICECHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial Goals
 
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AISyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.com
 
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
 
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
 
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerHow To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTV
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service provider
 
CALL ON ➥8923113531 🔝Call Girls Badshah Nagar Lucknow best Female service
CALL ON ➥8923113531 🔝Call Girls Badshah Nagar Lucknow best Female serviceCALL ON ➥8923113531 🔝Call Girls Badshah Nagar Lucknow best Female service
CALL ON ➥8923113531 🔝Call Girls Badshah Nagar Lucknow best Female service
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview Questions
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
 
Microsoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdfMicrosoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdf
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
 
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
 

Domain Driven Design Made Functional with Python

  • 1. Domain modelling with types using mypy Jean Carlo Machado
  • 2. Motivation / Idea ● I’m a fan of functional programming and Domain Driven Design ● used Haskell, Elm, and saw something valuable there ● But never managed to convert those ideas to business reality properly ● Hard to find material about it, rather you are a super expert or knows nothing at all
  • 4. Motivation / Idea ● Mypy bring this concepts to something more palpable than haskell ● The concepts are replicable to other languages, open-api, etc ● Fits as a glove with event based distributed systems ● Assumes some DDD and FP knowledge
  • 5. Goal Leave you inspired to seek more about the topic
  • 6. What is a type? Set of possible value that can be used as inputs or outputs of a function
  • 7. What is a type system? Formalize and enforce the otherwise implicit categories (types) the programmer uses. The main purpose of a type system is to reduce possibilities for bugs in computer programs. Type systems have other purposes as well, such as expressing business rules, enabling certain compiler optimizations, providing a form of documentation, etc.
  • 8. What is mypy? Mypy is a static type checker for Python 3 and Python 2.7. A tool that implements pep 484, types are optional No runtime validation/overhead Used by Google and Dropbox Facebook have their own versions of it
  • 9. Building blocks of the type-system algebra Using my startup idea to kill goodreads as example (okreads).
  • 10. Simple values - New type People don’t think in term of integers and string. They talk OrderId, customer name, etc Isbn10 = NewType('Isbn10', str) Isbn5 = NewType('Isbn5', str)
  • 11. Simple values - New type def download_book_by_isbn(isbn: Isbn10): ... isbn_string = "978-3-16-148410-0" isbn_type = Isbn10(isbn_string) download_book_by_isbn(isbn_type) download_book_by_isbn(isbn_string) mypy test.py test.py:14: error: Argument 1 to "download_book_by_isbn" has incompatible type "str"; expected "Isbn10" Found 1 error in 1 file (checked 1 source file)
  • 12. Combinations of values AND IsbnInfo = NamedTuple('IsbnInfo', [('isbn5', Optional[Isbn5]), ('isbn10', Optional[Isbn10])])
  • 13. Modelling choices OR Attribute undefined errors are one of the most common in programming Optional = Union[T, None]
  • 14. Modelling choices OR Also good for modeling different states in the domain. Types can also represent certainty. AIsbn = Union[Isbn5, Isbn10]
  • 15. Modelling choices OR Also good for modeling different states in the domain UnvalidatedBookData = NamedTuple('UnvalidatedBookData', [('author_data', Any), ('title', str)]) class ValidatedBookData: def __init__(self, data: UnvalidatedBookData): if len(data.title) < 3: raise Exception('Title should be bigger than 3 chars') self.title = data.title self.author_data = data.author_data PersistedBookData = NewType('PersistedBookData', 'ValidatedBookData') BookState = Union[UnvalidatedBookData, ValidatedBookData, PersistedBookData]
  • 16. Type Functions LoadOpenLibraryBookReference = Callable[[ExistingFile, Optional[LinesToLoadLimit]], BookReferences]
  • 17. Classes to do what the type-system cannot class ExistingFile: def __init__(self, location: str): if not os.path.exists(location): raise Exception("File does not exist") self.location = location
  • 18. Generics CommandData = TypeVar('CommandData') class Command(Generic[CommandData]): def __init__(self, data: CommandData, issuer: CommandIssuer): self.data = data self.issuer = issuer List is a wrapper of something generic.
  • 19. Putting it all together Rethinking the paradigm of modelling software We are biased towards OOP and RDBSs
  • 23. Workflows are composed by sub workflows
  • 25. Avoid flag types Status = Literal["unvalidated", "validated"] Book = NamedTuple('Book', [('author_data', Any), ('title', str), ('status', Status)]) def persist(book: Book): if book.status != 'validated': raise Exception("Cannot save unvalidated") #save here
  • 26. Every business subworkflow returns a new type UnvalidatedBookData = NamedTuple('UnvalidatedBookData', [('author_data', Any), ('title', str)]) ValidatedBookData = NewType('ValidatedBookData', UnvalidatedBookData) PersistedBookData = NewType('PersistedBookData', ValidatedBookData) def persist(book: ValidatedBookData) -> PersistedBookData: ...
  • 27. Avoid corrupting the model to keep fewer entities Review = NamedTuple('Review', [('title', Title) ,('author', Title)]) ## add activity approach 1 Review = NamedTuple('Review', [('title', Title) ,('author', Author), ('activity', Optional[Activity]) ]) # add activity approach 2 ReviewWithActivity = NamedTuple('ReviewWithActivity', [('Review', Review), ('activity', Activity) ]) # approach 3 QualifiedReview = NamedTuple(QualifiedReview, [...])
  • 28. Workflow - dependencies LoadOpenLibraryBookReference = Callable[['ExistingFile', Optional['LinesToLoadLimit']], BookReferences] LoadOpenLibraryBookData = Callable[[OpenLibraryBookReference], 'UnvalidatedBookData'] PersistBook = Callable[['ValidatedBookData'], PersistedBookData] class Workflow: def __init__(self, book_reference_loader: LoadOpenLibraryBookReference, book_data_loader: LoadOpenLibraryBookData, persist_book: PersistBook): self.book_reference_loader = book_reference_loader self.book_data_loader = book_data_loader self.persist_book = persist_book A OOP class constructor is a higher order function that injects types and returns an aggregated type(the object) with the state injected and the set of callables it contains
  • 29. Complete Workflow LoadOpenLibraryBookReference = Callable[['ExistingFile', Optional['LinesToLoadLimit']], BookReferences] LoadOpenLibraryBookData = Callable[[OpenLibraryBookReference], 'UnvalidatedBookData'] PersistBook = Callable[['ValidatedBookData'], PersistedBookData] class Workflow: def run(self, cmd: SyncOpenLibraryCmd) -> BookPersistedEvents: limit = None if cmd.limit: limit = LinesToLoadLimit(cmd.limit) for reference in self.book_reference_loader(ExistingFile(cmd.filename), limit): unvalidated_book_data = self.book_data_loader(reference) validated_book_data = ValidatedBookData(unvalidated_book_data) persisted_book = persist_book(validated_book_data) yield BookPersistedEvent(persisted_book_data=persisted_book)
  • 30. Errors from experience Business types should be valid all the time (owning its instantiation) def activity_from_dict(data: dict) -> Activity: image = EntitiesFactories.image_from_dict(data['image']) if 'image' in data else None labels: List[Label] = [] if data.get('labels'): labels = list(map(EntitiesFactories.label_from_dict, data['labels'])) return Activity(activityId=data['activityId'], title=data.get('title', ''), experienceId=data.get('experienceId', None), isAvailable=data.get('isAvailable', True), description=data.get('description', ''), price=price_from_dict(data['price']), rating=rating_from_dict(data.get('rating', {})), duration=duration_from_dict(data.get('duration', {})), isCertified=data.get('isCertified', False), isOnline=data.get('isOnline', True), labels=labels, isOriginal=is_original(data['activityId']), adpUrl=data.get('adpUrl', ''), image=image, type=data.get('type', ''), attributes=data.get('attributes', []))
  • 31. Use classes/objects for: Workflows Validation of integrity of value objects and aggregate roots Equality checks, merging aggregate roots Custom serialization
  • 32. Limitations of mypy - no recursive types Employee = NamedTuple('Employee', [('id', Id), ('manager', 'Employee')]) mypy tree.py tree.py:4: error: Cannot resolve name "Employee" (possible cyclic definition) Found 1 error in 1 file (checked 1 source file)
  • 33. Limitations of mypy no private constructors without hack no inheritance of union type or aliasing
  • 34. Key learnings When types are cheap you can model all valid states of your business with it. Every function that change something in the business should return a new type. Make illegal states impossible by modelling all valid ones in the type-system Strive to represent the system state by the type-system
  • 35. Complements and alternatives of mypy pyre - facebook pytype - google pyannotate - automatically annotate your functions (Dropbox) monkeytype: generate runtime annotations for types (instagram)
  • 36. Resources ● Podcast: Gradual Typing of Production Applications ● Example of mypy in GYG: Search service ● Our journey to type checking ● DDD at 10 years ● Okreads source applying this ideas
  • 37. The end Thanks for listening Just like the builders of Europe’s great Gothic cathedrals we’ve been honing our craft to the limits of material and structure. There is an unfinished Gothic cathedral in Beauvais, France, that stands witness to this deeply human struggle with limitations. It was intended to beat all previous records of height and lightness, but it suffered a series of collapses. Ad hoc measures like iron rods and wooden supports keep it from disintegrating, but obviously a lot of things went wrong. From a modern perspective, it’s a miracle that so many Gothic structures had been successfully completed without the help of modern material science, computer modelling, finite element analysis, and general math and physics. I hope future generations will be as admiring of the programming skills we’ve been displaying in building complex operating systems, web servers, and the internet infrastructure. And, frankly, they should, because we’ve done all this based on very flimsy theoretical foundations. We have to fix those foundations if we want to move forward. - Bartosz Milewski’s