Clean架构和DDD在什么情况下不是一个好主意?


我喜欢 DDD 和清洁架构背后的概念,但我觉得在某些情况下我可能只是做错了或者将它应用于正确类型的应用程序。
我正在为域实体(QueryGroup)添加更新操作,并添加了两种方法,如下所示简化显示:

 

  def add_queries(self, queries: list[QueryEntity]) -> None:
        """Add new queries to the query group"""
        if not queries:
            raise ValueError(
"Passed queries list (to <code>add_queries</code>) cannot be empty.")

        # Validate query types验证查询类型
        all_queries_of_same_type = len(set(map(type, queries))) == 1
        if not all_queries_of_same_type or not isinstance(queries[0], QueryEntity):
            raise TypeError(
"All queries must be of type QueryEntity.")

        # Check for duplicates检查重复项
        existing_values = {q.value for q in self._queries}
        new_values = {q.value for q in queries}

        if existing_values.intersection(new_values):
            raise ValueError(
"Cannot add duplicate queries to the query group.")

        # Add new queries
        self._queries = self._queries + queries

        # Update embedding更新嵌入
        query_embeddings = [q.embedding for q in self._queries]
        self._embedding = average_embeddings(query_embeddings)

    def remove_queries(self, queries: list[QueryEntity]) -> None:
       
"""Remove existing queries from the query group"从查询组中删除现有查询"""
        if not queries:
            raise ValueError(
               
"Passed queries list (to <code>remove_queries</code>) cannot be empty."
            )

        # Do not allow the removal of all queries. 不允许删除所有查询。
        if len(self._queries) <= len(queries):
            raise ValueError(
"Cannot remove all queries from query group.")

        # Filter queries过滤查询
        values_to_remove = [query.value for query in queries]
        remaining_queries = [
            query for query in self._queries if query.value not in values_to_remove
        ]
        self._queries = remaining_queries

        # Update embedding更新嵌入
        query_embeddings = [q.embedding for q in self._queries]
        self._embedding = average_embeddings(query_embeddings)


这一切都很好,但我的存储库是在领域对象上运行的,因此,尽管我已经获取了 ORM 模型查询组,但现在我需要再获取一次以更新它,并手动更新所有关联。

from sqlalchemy import select, delete, insert
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import selectinload

class QueryGroupRepository:
    # Assuming other methods like <strong>init</strong> are already defined

    async def update(self, query_group: QueryGroupEntity) -> QueryGroupEntity:
        """
        Updates an existing QueryGroup by adding or removing associated Queries.
       
"""
        try:
            # Fetch the existing QueryGroup with its associated queries
            existing_query_group = await self._db.execute(
                select(QueryGroup)
                .options(selectinload(QueryGroup.queries))
                .where(QueryGroup.id == query_group.id)
            )
            existing_query_group = existing_query_group.scalars().first()

            if not existing_query_group:
                raise ValueError(f
"QueryGroup with id {query_group.id} does not exist.")

            # Update other fields if necessary
            existing_query_group.embedding = query_group.embedding

            # Extract existing and new query IDs
            existing_query_ids = {query.id for query in existing_query_group.queries}
            new_query_ids = {query.id for query in query_group.queries}

            # Determine queries to add and remove
            queries_to_add_ids = new_query_ids - existing_query_ids
            queries_to_remove_ids = existing_query_ids - new_query_ids

            # Handle removals处理删除
            if queries_to_remove_ids:
                await self._db.execute(
                    delete(query_to_query_group_association)
                    .where(
                        query_to_query_group_association.c.query_group_id == query_group.id,
                        query_to_query_group_association.c.query_id.in_(queries_to_remove_ids)
                    )
                )

            # Handle additions处理添加
            if queries_to_add_ids:
                # 可选,确保查询存在。如果不存在,则创建它们
                existing_queries = await self._db.execute(
                    select(Query).where(Query.id.in_(queries_to_add_ids))
                )
                existing_queries = {query.id for query in existing_queries.scalars().all()}
                missing_query_ids = queries_to_add_ids - existing_queries

                # 如果有缺失的查询,则处理它们的创建
                if missing_query_ids:
                    #  您可能需要其他信息来创建新的查询实体。
                    # 为简单起见,我们假设您仅使用 ID 即可创建它们.
                    new_queries = [Query(id=query_id) for query_id in missing_query_ids]
                    self._db.add_all(new_queries)
                    await self._db.flush()  # Ensure new queries have IDs

                # Prepare association inserts准备关联插入
                association_inserts = [
                    {
"query_group_id": query_group.id, "query_id": query_id}
                    for query_id in queries_to_add_ids
                ]
                await self._db.execute(
                    insert(query_to_query_group_association),
                    association_inserts
                )

            # Commit the transaction提交事务
            await self._db.commit()

            # Refresh the existing_query_group to get the latest state
            await self._db.refresh(existing_query_group)

            return QueryGroupMapper.from_persistance(existing_query_group)

        except IntegrityError as e:
            await self._db.rollback()
            raise e
        except Exception as e:
            await self._db.rollback()
            raise e

我对这段代码的问题是,我们必须再次进行大量检查并处理添加/删除的不同情况,并且再次将验证添加到这里是个好主意。

如果我刚刚操作 ORM 模型,那么所有这些都会被跳过。
现在我了解了更多层和解耦的好处 - 但我只是不清楚在什么规模下或在什么情况下它会成为更好的权衡,而不是跨多层映射创建的更复杂和低效的代码。

(抱歉代码块太大,它们只是 LLM 生成的简单示例)

讨论1:
我认为,如果你身边没有领域专家,DDD 就不是一个好主意。我觉得 DDD 更适合记录和分发已知的领域知识,而不是探索一个还没有专家的全新领域。

讨论2:
总的来说,我建议将 DDD 应用于您的核心领域,即您可以产生竞争优势的核心业务。例如,事务脚本、crud 应用程序通常不需要 DDD。
老实说,如果你想更好地理解,我最好的建议是阅读 Vlad Khononov 的《学习领域驱动设计》。它还回答了你关于 DDD 的问题。

讨论3:
我会从封装的角度来考虑这个问题,以减少认知负荷。在代码库中比较简单或较小的区域,可能不需要那么多不同的层或边界,因为认知负荷已经很低了,但随着这些模块的大小或复杂性的增加,这些范例和架构就会变得很有价值,因为它们更容易被人类大脑消化。

如果严格(或执行不力)遵守某项原则导致代码难以理解,进而难以维护或修改,那么这是一个坏主意。

然而,棘手的部分是:在决定架构时,您处理的不是最终产品,而是产品其余部分将建立在其上的初始基础。现在,将所有层次和边界都规划出来可能会感觉开销更大,但将来可能会节省无数个小时的痛苦。

讨论4:
清晰clean架构和 DDD 本质上没有联系。它们满足不同的需求。领域驱动设计的核心前提(理解和建模业务领域,并使软件与领域的规则和语言保持一致)可以在没有清晰架构的情况下完成。无论你如何构建项目,在命名中使用领域语言可能总是一个好主意

讨论5:
我不知道为什么人们如此关注 DDD 模式,而最重要的部分是第 4 章,即战略部分。
我认为你对活动记录和存储库进行了奇怪的混合。我建议你只使用其中一种,并找到可以为你处理样板代码的工具。
您正在混合域、应用程序和持久性逻辑。您不应该像在这里一样重复验证。您的存储库不需要进行这些验证,它应该只提供数据访问,并且验证应该在应用程序或域层完成。存储库仅用于数据访问