域驱动设计外部系统和技术依赖

时间:2012-10-03 19:04:22

标签: domain-driven-design

我正在设计一个使用域驱动设计概念的系统,我正在努力解决一些问题。 “域”本质上是我工作的公司的业务系统。我也在使用依赖注入。因此,在我的模型中,我有与任何典型业务系统(员工,订单,发票,存款等)相关的事情。现在我正在尝试创建一个现金发布应用程序,用户(也就是员工)可以创建存款并将其应用于未付发票。我遇到的问题是我们还使用外部业务系统(Microsoft Dynamics Nav)来处理我们的会计事务。所以基本上我正在处理两个不同的数据库。因此,对于现金发布应用程序,我已经建模了域对象Deposit和DepositLine。我的域中还有一个IDepositRepository接口,负责持久存款。要从系统获得存款,我只想直接从数据库中获取。但是,为了创建存款,我必须使用Dynamics Nav Web服务,因为某些逻辑会在我不知道的幕后执行。我开始研究Anti Corruption层的概念,在该层中我可以将我的存款对象版本转换为适合Web服务的存款对象。所以这就是我现在想象的:

域层

- Models
    - Deposit
    - DepositLine
- Repositories
    - IDepositRepository

基础设施层

- Data
    - Repositories
        - DepositRepository
- DynamicsNav
    - Services
        - INavCashManagementService
    - Translators
        - IDepositTranslator
    - Adapters
        - INavAdapter

现在我想我可以这样实现DepositRepository:

public class DepositRepository
{
    private INavCashManagementService navCashManagementService;

    public DepositRepository(INavCashManagementService navCashManagementService)
    {
        this.navCashManagementService = navCashManagementService;
    }

    public Deposit GetDeposit(int id)
    {
        // use nhibernate to get directly from the database
    }

    public void SaveDeposit(Deposit deposit)
    {
        this.navCashManagementService.CreateDeposit(deposit);
    }
}

首先,这是一个合适的设计吗?我的下一个问题是用户也将不得不“发布”存款。 Nav Web服务也必须用于运行发布例程。但是,这更像是一个业务流程而不是持久性问题,因此我认为它不适合存储库。所以我想知道我应该如何/在哪里调用发布例程。我应该创建这样的域名服务:

public class CashPostingDomainService
{
    private INavCashManagementService navCashManagementService;

    public CashPostingDomainService(INavCashManagementService navCashManagementService)
    {
        this.navCashManagementService = navCashManagementService;
    }

    public void PostDeposits()
    {
        this.navCashManagementService.PostDeposits();
    }
}

我对域驱动设计的一个困惑是外部依赖。 CashPostingDomainService类现在是否对Nav具有外部依赖性?我知道实现不在域层中,但是接口本身不会使它成为依赖吗?发送电子邮件等其他技术问题也是如此。如果我有一个IEmailService接口并且想要在发布存款后发送电子邮件,我会将接口注入CashPostingDomainService类吗?或者这是应用程序工作流程的一部分?那么这些选项中的哪一个最有意义(如果有的话):

1

public class DepositController
{
    private ICashPostingDomainService cashPostingDomainService;
    private IEmailService emailService;

    public DepositController(
        ICashPostingDomainService cashPostingDomainService, 
        IEmailService emailService)
    {
        this.cashPostingDomainService = cashPostingDomainService;
        this.emailService = emailService;
    }

    public void PostDeposits()
    {
        this.cashPostingDomainService.PostDeposits();
        this.emailService.NotifyDepositsPosted();
    }
}

2

public class DepositController
{
    private ICashPostingDomainService cashPostingDomainService;

    public DepositController(
        ICashPostingDomainService cashPostingDomainService)
    {
        this.cashPostingDomainService = cashPostingDomainService;
    }

    public void PostDeposits()
    {
        this.cashPostingDomainService.PostDeposits();
    }
}

public class CashPostingDomainService
{
    private INavCashManagementService navCashManagementService;
    private IEmailService emailService;

    public CashPostingDomainService(
        INavCashManagementService navCashManagementService,
        IEmailService emailService)
    {
        this.navCashManagementService = navCashManagementService;
        this.emailService = emailService;
    }

    public void PostDeposits()
    {
        this.navCashManagementService.PostDeposits();
        this.emailService.NotifyDepositsPosted();
    }
}

感谢您的帮助!

1 个答案:

答案 0 :(得分:1)

  

这是一个合适的设计吗?

对我来说似乎很好。重要的是你的Repository不要忘记Nav的一面,让反腐败层处理它。对于类似的示例,您可能希望查看here

  

我知道实现不在域层中,但不是   接口本身使它成为依赖?

您可能有这种感觉,因为您的(假设不可知)服务界面的名称包含“Nav”。要反映可能将Nav 任何其他ERP作为实现的服务抽象,您应该将其重命名为ICashManagementService

  

如果我有一个IEmailService接口并且想要发送一次电子邮件   存款被张贴,我会将界面注入到   CashPostingDomainService类?或者那是否是其中的一部分   应用程序工作流程?

您的建筑决定是选择其中一个。

选项1.表示发送电子邮件是存款过帐域操作的固有部分。如果您使用域模块并在其他应用程序中重复使用,则发布存款将自动导致发送电子邮件,无论该应用程序是什么。这可能是您在上下文中做的正确的事情,或者您可能希望使事情更通用(例如,在操作后发送反馈但不在域服务中决定此反馈是否应该是邮件,日志文件等。)

选项2.表示在过帐存款后发生的事件序列是特定于应用程序的,即在用例级别而不是业务/域级别。由控制器(或应用程序服务)决定采取哪些动作 - 发送电子邮件或其他任何内容。因此,基于您的域层的不同应用程序可能决定采取不同的操作。这也意味着如果其中一些应用程序选择发送邮件,这些应用程序之间可能存在代码重复。