Flask Blog tutorial with Hexagonal Architecture(part 1)

Shahriyar Rzayev
Dev Genius
Published in
4 min readOct 7, 2022

--

Photo from:
http://thinkmicroservices.com/blog/2019/hexagonal-architecture.html

GitHub Repo of this project

> The second part of this series

> The third part of this series

The idea is to rewrite the official Flask blog tutorial with the help of Dependency Injection and follow the Hexagonal Architecture.

You can diff and look at the original code base from here: Flask blog tutorial code base

All statics and templates were grabbed from the original blog post.

This project should give an idea of how we can actually implement the Ports and Adapters(Hexagonal) architecture using Python, Flask, and DI.

The final project structure looks like this:

About Hexagonal Architecture

You can read it from the original author:

The Pattern: Ports and Adapters

About project dependencies

The project has 2 main dependencies:

Dependency Injector

Flask

Getting started

Now let’s dig into what we have and how we can convert this original flask blog tutorial code into a clean, maintainable hexagonal architecture.

Conceptually we need to separate our domain model from the outer world by centering it at the heart of our application. That means we need to first think about our domain models. Again not about the database, not about flask, not about docker, etc, think about what is going to be the heart of your application.

The best is to ask questions:

  • Q: What a heck is this app? — As for the official Flask tutorial this is a blog app, and it needs 2 things to work: the user to log in and the blog to be written.
  • Q: Who is the heck this user? What makes “something” a user? — “something” with a unique ID, username, and password can be considered as a User for us.
  • Q: What is a blog post then? — Blog post is going to have author_id, title, body, and maybe the created time. “Something” with that information would be considered a blog post.

Cool, we have defined our models, it is time to implement them.

Please keep in mind, the domain models are not the database models, we do not even think about database. Database is a detail.

Implement domain models

Our domain models are going to rest at: /src/domain/model path. So please create those folders.

Then create user.py and post.py which is going to represent User and Post respectively.

Notice the factory method for creating a User model and returning it back. Ideally, there should be a great validation step in this factory method, but we have omitted it as the original Flask tutorial also blinds it silently :)

Our Post model:

Quite simple, and quite neat.

Reasoning about ports.

As we are going to follow Ports and Adapters architecture, we need to think about Ports first as this is the place that is getting to be implemented and used say by REST API.

I put the ports code inside the domain ports folder /src/domain/ports.

Ports are interfaces to be implemented by adapters, which you will see in a moment.

Questions:

  • Q: How we are going to persist our user and post data? — Okay, we can have a simple Repository pattern, which is an interface or abstract class accepting database connection.

Create repository.py file inside the ports and put the following code:

Basically, our Repo is going to execute the SQL statements and then call the DB commit. We are not using any kind of ORM here, as the original Flask Blog tutorial also only uses raw SQLs with SQLite.

Q: How the outside world(CLI, REST, anything) is going to register and log in to the User? — We can have the simplest ever Command or Service pattern, let’s just call it user_service.py:

Now please, notice that the UserService accepts the RepositoryInterface as an argument, as a dependency.

Using user_repo it is going to persist the actual User data.

Also, notice how the user model was created using user_factory inside the create method, and only after that the user data get constructed to be inserted into the database.

This is how we invert the dependency of the storage mechanism as well, our User model does not depend on the database to survive, but the database action requires our User to do its work.

  • How about managing Posts? Here you go, post_service.py:

PostService is accepting again the abstract repository for persisting the post data. But we have more interesting things here.

Let me explain more about the idea behind CreatePostInputDto, UpdatePostInputDto, DeletePostInputDto.

Again the questions:

  • What kind of information do we need to create a Post domain model — Basically, post_factory() requires author_id, body, and title, created time will be automatically instantiated. CreatePostInputDto(data transfer object) simply encapsulates and validates this information, using its data the post_factory creates Post, and then it is inserted into the database.
  • What kind of information do we need to update the saved post in the database? — UpdatePostInputDto encapsulates and again validates post id, title, and body
  • What kind of information do we need to delete the saved post from the database? — We can find the post by id and then remove it. DeletePostInputDto simply encapsulates the id for us.

But why, do we need those DTOs? If you accept something from outside as an Input, and if you return something back as Output, it is always better to restrict, validate and also give some shape to the accepted and returned data. Think about it as a Request/Response model in web frameworks. You accept some data as part of the request object, then you construct some Response model and return it to the user.

I am going to put these DTOs in the /src/domain/ports/__init__.py:

In conclusion the first part:

  • We have implemented the simple domain model, without any domain events or other fancy things.
  • We have implemented all ports of the Hexagonal Architecture.

We have no tests as well, because original Flask Blog tutorial brutally kicked the tests out :D

--

--

Azerbaijan Python User Group lead — True Pythonista. Open Source enthusiast.