【问题标题】:Techniques for dealing with anemic domain model处理贫血域模型的技术
【发布时间】:2010-10-11 04:54:13
【问题描述】:

我已经阅读了一些关于贫乏领域模型和关注点分离的问题。在贫血的域对象上执行/附加域逻辑的最佳技术是什么?在我的工作中,我们有一个非常贫乏的模型,我们目前正在使用“帮助”类来执行域对象上的数据库/业务逻辑。例如:

public class Customer
{
    public string Name {get;set;}
    public string Address {get;set;}
}

public class Product
{
    public string Name {get;set;}
    public decimal Price {get;set;}
}

public class StoreHelper
{
    public void PurchaseProduct(Customer c, Product p)
    {
         // Lookup Customer and Product in db
         // Create records for purchase
         // etc.
    }
}

当应用需要进行购买时,它会创建 StoreHelper,并调用域对象上的方法。对我来说,客户/产品知道如何将自己保存到存储库是有意义的,但您可能不希望域对象上的 Save() 方法。对于像 Customer.Purchase(Product) 这样的方法也是有意义的,但这是将域逻辑放在实体上。

以下是我遇到的一些技术,不确定哪些是好/坏:

  1. Customer 和 Product 继承自“Entity”类,该类以通用方式提供基本的 CRUD 操作(可能使用 ORM)。
    • 优点:每个数据对象都会自动获取 CRUD 操作,但随后会绑定到数据库/ORM
    • 缺点:这并没有解决对象上的业务操作问题,而且还将所有域对象绑定到可能不合适的基本实体
  2. 使用帮助类来处理 CRUD 操作和业务逻辑
    • 让 DAO 用于“纯数据库”操作,而单独的业务助手用于更具体的业务操作是否有意义?
    • 为此使用非静态或静态辅助类更好吗?
    • 优点:域对象不绑定到任何数据库/业务逻辑(完全贫乏)
    • 缺点:不是很 OO,在应用程序代码中使用帮助器不是很自然(看起来像 C 代码)
  3. 在实体具有保存到任意存储库的方法时使用双重调度技术
    • 优点:更好地分离关注点
    • 缺点:实体附加了一些额外的逻辑(尽管它是解耦的)
  4. 在 C# 3.0 中,您可以使用扩展方法将 CRUD/业务方法附加到域对象而不接触它
    • 这是一种有效的方法吗?有哪些优点/缺点?
  5. 其他技术?

处理此问题的最佳技术是什么?我对 DDD 很陌生(我正在阅读 Evans 的书——所以也许这会让我大开眼界)

【问题讨论】:

    标签: domain-driven-design business-logic


    【解决方案1】:

    为了避免贫血模型,重构你的助手类:

    逻辑如下:
    "Customer.PurchaseProduct(Product product, Payment payment)",
    "Customer.KillCustomer(人杀手,武器武器)"
    应该存在于“客户”域对象中。

    逻辑如下:
    "Customer.IsCustomerAlive()"
    "Customer.IsCustomerHappy()"
    应该去规范。

    逻辑如下:
    "Customer.Create()",
    “客户.Update()”
    显然应该去存储库。

    逻辑如下:
    “Customer.SerializeInXml()”
    "Customer.GetSerializedCustomerSizeInBytes()"
    应该去服务。

    复杂的构造函数应该去工厂。

    这就是我的看法。如果有人能评论我对 DDD 方法的理解,我会很高兴。


    编辑:

    有时 - 贫血域模型shouldn't be avoided

    编辑了我的答案,补充说 DDD 不是拾取和丢弃模式。
    DDD 是关于我们的思维方式。

    【讨论】:

    • 似乎有很多不同的类只是为了处理一个客户。为什么不将大部分内容放在一个类中,使用一个服务来处理任何复杂的事情?
    • 我的回答太老了。 :D
    • @LuckyLindy 主要是因为 DDD 是关于在领域专家和程序员之间建立桥梁。领域模型不应包含技术内容,否则将无法存在无处不在的语言。为了移出技术性的东西——我们必须抽象它。抽象一些东西总是会膨胀代码库。
    • 该死的,它仍然得到了投票。回答听起来好像很容易机械地避免贫血域模型。不幸的是 - 这不是真的。
    【解决方案2】:

    Martin Fowler 写了很多关于域模型的文章,包括 anemic domain models。他还简要描述了许多领域模型和数据库的设计模式(和 UML 类图),可能会有所帮助:Catalog of "Patterns of Enterprise Application Architecture"

    我建议查看Active RecordData Mapper 模式。从您的问题描述中,听起来您的帮助类包含域/业务规则 数据库实现细节。

    Active Record 会将助手的域逻辑和数据库代码移动到其他域对象中(例如您的Entity 基类)。数据映射器会将助手的域逻辑移动到域对象中,并将数据库代码移动到单独的映射对象中。这两种方法都比过程式帮助类更面向对象。

    Eric Evans 的“领域驱动设计”一书非常出色。它有点干,但绝对值得。 InfoQ 有一个"Domain Driven Design Quickly" mini-book,它很好地介绍了 Evans 的书。此外,“快速领域驱动设计”以免费 PDF 格式提供。

    【讨论】:

      【解决方案3】:

      我一直认为贫血域模型是一种反模式。很明显,客户会购买产品,这种能力可以通过接口实现来生成

      Interface IPurchase
            Purchase(Product);
      

      ,因此您的任何域对象都可以根据需要实现它。通过这种方式,您可以将功能引入您的域对象 - 这正是它应该在的地方。

      【讨论】:

        【解决方案4】:

        您没有提到的一种方法是使用 AOP 来处理您的数据访问。我最近使用这种方法的一个例子(尽管为了发布目的大大简化了)是我有一个 Account 域实体,它有一个 debit 方法,封装了从帐户中成功借记所需的业务逻辑.

        注意所有代码都是带有 AspectJ AOP 表示法的 Java...

        public boolean debit(int amount) {
            if (balance - amount >= 0) {
                balance = balance - amount;
                return true;
            }
            return false;
        }
        

        将适当的存储库注入到我的方面,然后我使用切入点来拦截对该方法的调用...

        pointcut debit(Account account,int amount) :
            execution(boolean Account.debit(int)) &&
            args(amount) &&
            target(account);
        

        ...并应用了一些建议:

        after(Account account, int amount) returning (boolean result)  : debit(account,amount) {
            if (result) getAccountRepository().debit(account, amount);
        }
        

        在我看来,这很好地分离了关注点,并允许您的域实体完全专注于应用程序的业务逻辑。

        【讨论】:

          猜你喜欢
          • 2010-11-04
          • 2018-12-14
          • 2010-12-20
          • 1970-01-01
          • 2010-12-26
          • 1970-01-01
          • 2014-06-12
          • 2011-09-19
          • 2010-10-28
          相关资源
          最近更新 更多