2

I'm trying to create a Flask application with DDD patterns.

One of the core principles of DDD is to separate the domain from the persistence (infrastructure). I have defined my domain models in a module, and am going to create a repository in the infrastructure module.

However, i can't seem to find any information as to how to persist POJOs in Python. I have looked at sqlalchemy, but with sqlalchemy you create persistence models. Creating domain models as persistence models is an anti pattern in DDD.

What persistence options are available for a use case like this? More specifically, what persistence options can i use to persist models defined as POJOs?

Mike Hawkins
  • 2,144
  • 5
  • 24
  • 42

2 Answers2

3

You can choose from a few strategies for this case. In many cases, it's not a problem that DTO for your aggregate is DB Model. If you want separation you can create an interface for this DTO and implement it in the repository as a model.

class User:
    id: int
    name: Text

class PlainUser(User):
    def __init__(self, name):
        self.id = autogenerate_id()
        self.name = name

class DBUser(db.Model, User):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), nullable=False)

Another strategy is to map sqlalchemy tables on POPO (btw. In Java you have POJO, Plain Old Java Object. In python you have POPO, Plain Old Python Object). So you declare table and use mappers instead of declarative mode. The third option is to use a library for mapping DB Model to POPO like dry-python/mappers. Also if you want to separate DDD components you need to think about separate migrations. To answer more deeply on this question (and another on StackOverflow) I wrote this post Persistency of DDD aggregate. Hope it will be helpful to you with other obstacles with DDD components.

lzukowski
  • 191
  • 1
  • 5
0

To create separated SQLAlchemy model for domain and persistance you should use mapper, instead of declarative mapping style. Example:

# entities.py
class Table:
    def __init__(self, table_id: int):
        self.id = table_id

class Restaurant:
    def __init__(self, restaurant_id: int, tables: List[Table]):
        self.id = restaurant_id
        self.tables = tables

# orm.py
from sqlalchemy import Boolean, Column, ForeignKey, Integer, MetaData, create_engine
from sqlalchemy import Table as sa_Table
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import mapper, relationship
from entities import Restaurant, Table

SQLALCHEMY_DATABASE_URI = "sqlite:///" # your db uri

engine = create_engine(SQLALCHEMY_DATABASE_URI)
metadata = MetaData(bind=engine)
Base = declarative_base(metadata=metadata)

restaurant = sa_Table(
    "restaurant",
    metadata,
    Column("id", Integer, primary_key=True, autoincrement=True),
)

table = sa_Table(
    "table",
    metadata,
    Column("id", Integer, primary_key=True, autoincrement=True),
    Column("restaurant_id", Integer, ForeignKey("restaurant.id"))
)

def run_mappers():
    """
    Provides mapping between db tables and domain models.
    """
    mapper(
        Restaurant,
        restaurant,
        properties={"tables": relationship(Table, backref="restaurant")},
    )
    mapper(Table, table)

run_mappers() # it should be executed in the app runtime

If you need more context, here is my repo implementing full python project in DDD style (using Port and adapters architecture): https://github.com/jorzel/opentable I have recently written a blog post about it: https://jorzel.hashnode.dev/persistence-and-domain-model-separation-using-sqlalchemy-orm. There is also a great book (in python) covering this topic: https://github.com/cosmicpython/book

jorzel
  • 1,216
  • 1
  • 8
  • 12