avatarFlorian Kromer

Summary

The provided content discusses the translation of "Architecture Patterns with Python" into FastAPI, detailing the porting of Flask-based example code to FastAPI while adhering to Domain Driven Design (DDD) principles.

Abstract

The content outlines the process of adapting example code from the book "Architecture Patterns with Python," which originally used Flask, to FastAPI. This adaptation is done within the context of Domain Driven Design, demonstrating how FastAPI can be utilized to implement advanced DDD concepts. The author has created a series of GitHub branches corresponding to the chapters of the book, showcasing the iterative development of a shopping domain application using DDD. The content emphasizes the ease of swapping out components, such as the microservice entry points and Docker configurations, while maintaining the core business logic. It also highlights the importance of abstractions, the repository pattern, service layers, and the Unit of Work pattern in achieving clean design and testability. The author provides insights into the development workflow, the use of Makefiles, and the considerations for running the examples on different operating systems. Additionally, the content points out the need for a service bus to fully realize advanced DDD with FastAPI.

Opinions

  • The author believes that the example code effectively demonstrates the interchangeability of web frameworks when a clean design is maintained.
  • There is a strong endorsement of the book "Architecture Patterns with Python" for its explanation of DDD and its focus on testability and maintainability.
  • The author suggests that cloud-native backend development is more straightforward on Linux machines compared to Windows or macOS.
  • The author expresses a personal challenge with the development experience on Windows, hinting at rough edges despite using WSL2 with Docker Desktop.
  • The author is open to receiving feedback from macOS users regarding their development experience.
  • There is an opinion that the repository pattern and service layer are crucial for decoupling domain logic from web frameworks and databases, thus enhancing testability.
  • The author points out trade-offs in design choices, such as the complexity introduced by the repository pattern and the service layer.
  • The author identifies a gap in the Python ecosystem for a service bus, which is a significant component for DDD implementations, and suggests that its absence limits the potential of FastAPI in advanced DDD scenarios.

FastAPI Microservice Patterns: Domain Driven Design

“Architecture Patterns with Python” translated into FastAPI

Book cover of Architecture Patterns with Python.

Last update: 2th Sept 2023

Get 30 days of access to the example code of the overall blog post series on GitHub.

Note: In addition to the source code I’m pointing at in this post I decided to add a short summary to each chapter to give context which will help to understand the code. Feel free to visit again in the future. — Last edit, 6th August 2023.

Spoiler…

If you’ve already read this blog post scroll down to the end of the article to learn What’s missing to make FastAPI shine in advanced Domain Driven Design?

tl;dr

This post is about the example code from Architecture Patterns with Python (book as code) which uses Flask and which I ported to FastAPI.

The example code is structured in an iterative manner means an exemplary application in the “shopping domain” is build up using a Domain Driven Design approach. The source code repository has several branches including branches which show the code state of corresponding chapters chapter_XY_... .

You’ll figure out that the Flask and FastAPI versions of the code are almost identical. The only things which differ are the microservice entrypoints (flask_app.py vs. fastapi_app.py), the Dockerfiles as well as the docker-compose.yml file. This code is a great example of how easy it is to change a specific part of the system easily when sticking to clean design.

You can find the final version of the FastAPI port of the code in the branch appendix_fastapi of my source code repo fork: fkromer/code (appendix_fastapi) .

About the book

The book explains the concepts of Domain Driven Design (DDD) in a Test Driven Development (TDD) style with examples specific to Flask. Domain Driven Design W.r.t. TDD the book does not focus on testing but on how to design the code to enable testability which improves maintainability, extensibility, evolution, etc. of the production code. I higly recommend you to read this book!!!

There is an appendix about how the overall code would look like for Django as well. However as there is no appendix for FastAPI (yet) I decided to port the example code to FastAPI.

A note for MacOS and Windows users

The example code has been written for Linux in the first place as well. Cloud-native backend development is far more straight forward on Linux machines than on machines running Windows or macOS. Be aware of the fact that your development workflow will not be really smooth on Windows. I try my best myself using WSL2 with Docker Desktop on Windows… however there are a lot of rough edges around. I cannot really say something about the development experience on macOS. I’d be happy about personal messages w.r.t. this topic ;) A nice intro into issues with Windows is e.g. the post about (WSL2:) Why you should use real Linux instead. However in case you want to run the examples on Windows or macOS you’ve to consider some additional setup.

The book’s example code makes use of Makefiles for spinning up the code. macOS as well as Windows does not support Makefiles out-of-the-box. On Windows the probably easiest way to enable running the examples with make is by installing chocolatey like described here and install make with choco install make . On macOS the easiest option to get make is installation via brew. If everything went just fine you should be able to execute make and see make: *** No targets specified and no makefile found. Stop.

About how to execute the example code

The example code makes use of docker to wrap the microservice, database, etc. docker-compose to orchestrate the docker containers and make to execute docker-compose . The Makefile in the root directory of the project contains all the make targets ( make ... ) which are available and one can easily figure out what is executed in detail. In most cases a single make up should build the image (make build) and run the containers in “detached mode”. To teardown an example execute make down.

I’ve ported the code in quick and dirty fashion… no guarantees that everything works perfectly fine and that my fork gets out-of-sync w.r.t. the book’s repo 😜 I’m happy about PRs 😉

About how to understand the example code

I highly recommend to read the book chapters which correspond to specific branches of the source code before executing the example code. After that you’ll learn most effectively if you play around with the FastAPI microservice RESTful API via localhost:5005/docs, the PostgreSQL database using a client like e.g. pgAdmin and Redis via the redis-cli. To understand the development workflow using docker-compose it will be valuable to modify and debug the microservice code with the IDE of your choice.

Chapter 1: Domain Modelling

The example code for this chapter (branch: chapter_1_domain_model) is independent of the microservice/web framework used and can be used as is with FastAPI.

Here is the reference to the corresponding book chapter.

The chapter in a nutshell

  • The domain is a fancy way of saying the problem you’re trying to solve.
  • A model is a map of a process or phenomenon that captures a useful property.
  • The domain model is the mental map that business owners have of their businesses.
  • In a nutshell, domain driven design (DDD) says that the most important thing about software is that it provides a useful model of a problem. If we get that model right, our software delivers value and makes new things possible.
Visualization of a domain model in the book.

In the book it’s not explained what DDD is. It’s just explained how to implement DDD in Python. For completion I’ve added the definitions for the different kinds of models from Wikipedia

  • An entity is an object defined not by its attributes, but its identity. As an example, most airlines assign a unique number to seats on every flight: this is the seat’s identity.
  • A value object is an immutable object that contains attributes but has no conceptual identity. When people exchange business cards, for instance, they only care about the information on the card (its attributes) rather than trying to distinguish between each unique card.
  • Models can also define events (something that happens). A domain event is an event that domain experts care about.
  • Models can be bound together by a root entity to become an aggregate. Objects outside the aggregate are allowed to hold references to the root but not to any other object of the aggregate. The aggregate root checks the consistency of changes in the aggregate. Drivers do not have to individually control each wheel of a car, for instance: they simply drive the car. In this context, a car is an aggregate of several other objects (the engine, the brakes, the headlights, etc.).

and this blog post about event vs. command:

  • A command is an object that is sent to the domain for a state change which is handled by a command handler (in the service layer).
  • An event is a statement of fact about what change has been made to the domain state.

The book is about a service of a furniture store for allocating stock, called allocation service.

Context diagram for the allocation service.

The domain model consists of the models

Product class.
OrderLine dataclass.
Batch class.

, events

Events of the domain model.

and commands

Commands of the domain model.

Chapter 2: Repository Pattern

The example code for this chapter (branch: chapter_2_repository) is independent of the microservice/web framework used and can be used as is with FastAPI.

Here is the reference to the corresponding book chapter.

The pattern in a nutshell

This chapter is about applying the repository pattern. Before the pattern is applied the domain “components” are connected directly to the database. After the pattern has been applied the database is decoupled from the domain via an abstract and a concrete repository.

Before and after applying the repository pattern.

This means one introduces dependency injection with the aim of decoupling the database dependency from the domain model (business logic). Instead of implicitly, silently depending on the database it is explicitly injected into the domain model.

Layered architecture (domain model/business logic depends on database layer, outward flowing dependency).
Onion architecture (domain model does not depend on database layer).

Trade-offs

Trade-offs of the repository pattern.

Chapter 3: Abstractions

The example code for this chapter (branch: chapter_3_abstractions) is independent of the microservice/web framework used and can be used as is with FastAPI.

Here is the reference to the corresponding book chapter.

The chapter in a nutshell

This chapter explains how decoupling via abstractions and dependency injection help to be able to apply test driven development increase testability and allow test driven development in DDD.

System A is coupled to system B.
System A is decoupled from system B via an abstraction.

Chapter 4: Service layer

You can find the FastAPI port for branch: chapter_4_service_layer in the branch: chapter_4_service_layer_fastapi.

Here is the reference to the corresponding book chapter.

The pattern in a nutshell

After apllying the repository pattern (chapter 2) the code can be visualized as follows:

After applying the repository pattern (with tests).

This chapter introduces the service layer to decouple the web framework (Flask, or in our case FastAPI) and the tests from the domain and repositories.

After adding the service layer.

Trade-offs

Trade-offs of adding a service layer.

Chapter 5: TDD in High Gear and Low Gear

You can find the FastAPI port for branch: chapter_05_high_gear_low_gear in the branch: chapter_5_high_gear_low_gear_fastapi.

Here is the reference to the corresponding book chapter.

Chapter 6: Unit of Work Pattern

You can find the FastAPI port for branch: chapter_06_uow in the branch: chapter_6_uow_fastapi.

Here is the reference to the corresponding book chapter.

Chapter 7: Aggregates and Consistency Boundaries

You can find the FastAPI port of the branches branch: chapter_07_aggregate and branch: chapter_07_noinheritance in the branch: chapter_07_aggregate_fastapi as well as in the branch: chapter_07_noinheritance_fastapi.

Here is the reference to the corresponding book chapter.

Chapter 8: Events and the Message Bus

You can find the FastAPI port of the two branches branch: chapter_08_events_and_message_bus and branch: chapter_08_external_events in the branch: chapter_08_events_and_message_bus_fastapi as well as branch: chapter_08_external_events_fastapi.

Here is the reference to the corresponding book chapter.

Chapter 9: Going to Town on the Message Bus

You can find the FastAPI port of the branch: chapter_09_all_messagebus in the branch: chapter_09_all_messagebus_fastapi.

Here is the reference to the corresponding book chapter.

Chapter 10: Commands and Command Handler

You can find the FastAPI port of the branch: chapter_09_all_messagebus in the branch: chapter_09_all_messagebus_fastapi.

Here is the reference to the corresponding book chapter.

Chapter 11: Event-Driven Architecture - Using Events to Integrate Microservices

You can find the FastAPI port for branch: chapter_11_external_events in the branch: chapter_11_external_events_fastapi.

Here is the reference to the corresponding book chapter.

Chapter 12: Command and Query

You can find the FastAPI port for branch: chapter_12_cqrs in the branch: chapter_12_cqrs_fastapi.

Here is the reference to the corresponding book chapter.

Chapter 12: Command and Query

You can find the FastAPI port for branch: chapter_12_cqrs in the branch: chapter_12_cqrs_fastapi.

Here is the reference to the corresponding book chapter.

Chapter 13: Dependency Injection (and Bootstrapping)

You can find the FastAPI port for branch: chapter_13_dependency_injection in the branch: chapter_13_dependency_injection_fastapi.

Here is the reference to the corresponding book chapter.

Python is lacking a service bus

I’ve noticed that to implement DDD using FastAPI without the need to reinvent the wheel using proprietary code a major component for DDD implementations is missing… a service bus.

Learn more about this in my blog post What’s missing to make FastAPI shine in advanced Domain Driven Design?

Fastapi
Cloud Native
Python3
Design Patterns
Docker Compose
Recommended from ReadMedium