DDD与Python:我做对了吗?

DDD与Python:我做对了吗?,python,sqlalchemy,architecture,domain-driven-design,marshmallow,Python,Sqlalchemy,Architecture,Domain Driven Design,Marshmallow,我试图在Python项目中使用域驱动设计(DDD),但它看起来像很多样板代码。我想我走错了路 我有三个文件,每个文件都为每个目的定义了项目。感觉太多了。此外,我也经常在字典之间转换,但我试图把目的分开 这个主题不应该是基于观点的,因为我试图遵循DDD方法,应该有一个遵循的模式 以下代码的相关部分。请仔细查看项目存储库 /domain/item.py /model/item.py /schema/item.py /repository/item.py 要想拥有一个干净的体系结构而不增加开销,我该怎

我试图在Python项目中使用域驱动设计(DDD),但它看起来像很多样板代码。我想我走错了路

我有三个文件,每个文件都为每个目的定义了项目。感觉太多了。此外,我也经常在字典之间转换,但我试图把目的分开

这个主题不应该是基于观点的,因为我试图遵循DDD方法,应该有一个遵循的模式

以下代码的相关部分。请仔细查看
项目存储库

/domain/item.py

/model/item.py

/schema/item.py

/repository/item.py


要想拥有一个干净的体系结构而不增加开销,我该怎么做?

为了回答您的问题,我写了一篇博文,您可以在这里找到:。 现在您只有代码片段,稍后我将添加一些说明:-)

但一般来说,DDD应该是一个独立的组件,具有由纯数据对象进行通信的外观。此外观是一个应用程序服务。在我的例子中,它是命令处理程序和查询处理程序。大多数测试都是使用facade的BDD测试。有时,对于复杂的域逻辑,您可以对聚合/UnitOfWork使用单元测试。 您的应用程序架构将DDD元素拆分为我不喜欢的不同包。使用这种方法,您将失去对组件边界的控制。您需要从该组件获得的所有内容都应导出到init.py。 通常,它是带有命令的命令处理程序。如果需要数据视图,请使用查询处理程序。使用可能的事件注册事件侦听器

如果您不确定是否需要所有这些东西,可以从外观上的BDD测试和内部非常简化的实现开始。因此,命令处理程序具有直接使用DTO的业务逻辑。稍后,如果事情变得复杂,您可以轻松地重构。但适当的界限是成功的关键。另外,请记住,如果您觉得所有这些元素和代码都是开销,那么可能不需要DDD方法。也许它不符合DDD的要求

下面是一个关于组件包结构的代码片段的小示例。我用这样的方式: -迁移/ -app.py -commands.py -events.py -例外情况.py -repository.py -service.py -uow.py

在迁移中,我更喜欢将alembic与此特定组件的分支一起使用。因此,项目中将不依赖于其他组件

py是带有依赖注入的容器的位置。它主要用于向应用程序服务和存储库依赖项注入适当的存储库

对于其余的模块,我将在这里给出一些代码片段

commands.py

@dataclass
类创建(命令):
命令id:CommandID=field(默认工厂=uuid1)
时间戳:datetime=field(默认工厂=datetime.utcnow
service.py

类命令处理程序:
def uuu init uuu(self,repository:repository)->无:
self.\u repository=存储库
self.\u侦听器:列表[侦听器]=[]
super()。\uuuu init\uuuuu()
def寄存器(self,listener:listener)->无:
如果侦听器不在self中。\u侦听器:
self.\u listeners.append(侦听器)
def取消注册(自我,侦听器:侦听器)->无:
如果侦听器在self中。\u侦听器:
self.\u侦听器。删除(侦听器)
@安全的
@单一分派方法
def句柄(self,command:command)->可选[事件]:
uow:UnitOfWork=self.\u repository.get(command.uow\u id)
事件:事件=app\u事件(self.\u句柄(命令,uow),命令)
对于self中的侦听器。\u侦听器:
侦听器(事件)
自存储库保存(uow)
返回事件
@安全的
@句柄。注册(创建)
def create(自我,命令:创建)->事件:
uow=UnitOfWork.create()
自存储库保存(uow)
已创建返回(command.command\u id,uow.id)
@单一分派方法
def_handle(self,c:Command,u:UnitOfWork)->UnitOfWork.Event:
引发未实现的错误
@_handle.register(UpdateValue)
def(self,command:UpdateValue,uow:UnitOfWork)->UnitOfWork.Event:
返回uow.update(命令值)
uow.py

UnitOfWorkID = NewType('UnitOfWorkID', UUID)


class UnitOfWorkDTO:
    id: UnitOfWorkID
    value: Optional[Text]


class UnitOfWork:
    id: UnitOfWorkID
    dto: UnitOfWorkDTO

    class Event:
        pass

    class Updated(Event):
        pass

    def __init__(self, dto: UnitOfWorkDTO) -> None:
        self.id = dto.id
        self.dto = dto

    @classmethod
    def create(cls) -> 'UnitOfWork':
        dto = UnitOfWorkDTO()
        dto.id = UnitOfWorkID(uuid1())
        dto.value = None
        return UnitOfWork(dto)

    def update(self, value: Text) -> Updated:
        self.dto.value = value
        return self.Updated()
repository.py

类或存储库(存储库):
定义初始化(self,session:session):
self.\u session=会话
self.\u query=self.\u session.query(UnitOfWorkMapper)
def get(自身,uow\U id:UnitOfWorkID)->UnitOfWork:
dto=self.\u query.filter\u by(uuid=uow\u id).one\u或\u none()
如果不是dto:
未找到提升(uow\U id)
返回工作单元(dto)
def保存(自身,uow:UnitOfWork)->无:
self.\u session.add(uow.dto)
self.\u session.flush()
实体\u t=Table=Table(
“实体”,
元,
列('id',整数,主键=True,自动递增=True),
列('uuid',字符串,unique=True,index=True),
列('value',字符串,null=True),
)
UnitOfWorkMapper=映射器(
工作单位:,
实体,,
性质={
“id”:实体\u t.c.uuid,
“值”:实体\u t.c.value,
},
列前缀='''u db'列'',
)


这个例子的完整来源你可以在这里找到

如果你的代码是功能性的,并且你只是在寻找反馈,那么这可能属于代码复查堆栈交换。非常感谢。我期待着仔细查看你的博客文章。它是alrady书签。:-)请在这里添加重要的内容。好的。我用文章中的片段扩展了这个答案。
"""
Persistent model for SQLAlchemy
"""
class ItemModel(DefaultModel):
    __tablename__ = 'items'
    name = Column(Text)
"""
Schema for Marshmallow
"""
class ItemSchema(Schema):
    name = fields.Str(required=True)
class ItemRepository:

    def get_one(item_id):
        # ...
        model = session.query(ItemModel).filter_by(item_id=item_id).first()
        return ItemDomain.from_dictionary(dict(model))

    def add_one(item: ItemDomain):
        # ...
        item = item.to_dictionary()
        ItemSchema().load(item)  # validation: will raise an exception if invalid
        model = ItemModel()
        model.from_dictionary(item)
        session.add(model)
        # ...
UnitOfWorkID = NewType('UnitOfWorkID', UUID)


class UnitOfWorkDTO:
    id: UnitOfWorkID
    value: Optional[Text]


class UnitOfWork:
    id: UnitOfWorkID
    dto: UnitOfWorkDTO

    class Event:
        pass

    class Updated(Event):
        pass

    def __init__(self, dto: UnitOfWorkDTO) -> None:
        self.id = dto.id
        self.dto = dto

    @classmethod
    def create(cls) -> 'UnitOfWork':
        dto = UnitOfWorkDTO()
        dto.id = UnitOfWorkID(uuid1())
        dto.value = None
        return UnitOfWork(dto)

    def update(self, value: Text) -> Updated:
        self.dto.value = value
        return self.Updated()