首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >领域驱动设计( DDD )应用层

领域驱动设计( DDD )应用层
EN

Stack Overflow用户
提问于 2017-03-19 01:50:37
回答 4查看 7.2K关注 0票数 8

我一直试图建立一个基于DDD的应用程序,但我有一些问题。

我有一些层:-表示层- MVC -应用层-域层.

首先,我想知道我是否可以在ApplicationLayer中做到这一点(get家庭信息> get缺省消息信息>发送电子邮件>更新数据库):

代码语言:javascript
复制
public ApproveFamilyOutput ApproveFamily(ApproveFamilyInput input)
        {
            Family family = _familyRepository.GetFamily(input.Username);
            family.Approve();

            DefaultMessage defaultMessage = _defaultMessageRepository.GetDefaultMessage(MessageTypes.FamilyApproved);

            _email.Send(family.GetEmail(), defaultMessage.Subject, defaultMessage.Message);

            _familyRepository.Update(family);
            bool isSaved = _familyRepository.Save();

            return new ApproveFamilyOutput()
            {
                Errors = Helper.GetErrorIfNotSaved(isSaved)
            };
        }

我想得好吗?应用层负责做这项工作吗?

第二个问题是:我需要根据用户拥有的权限向表示层发送一些数据。这些特权在数据库中定义。对象族具有名称、LastName、PhoneNumber、电子邮件属性,用户可以显示/隐藏每个值。我怎么处理这件事?

我可以在应用层中这样做吗?

代码语言:javascript
复制
public GetFamilyOutput GetFamily(GetFamilyInput input)
        {
            Family family = _familyRepository.GetFamily(input.Username);

            FamilyConfiguration familyConfiguration = _familyConfigurationRepository.GetConfigurations(family.Id);

            //ProcessConfiguration will set to null the properties that I cannot show
            family.ProcessConfiguration(familyConfiguration);

            return new GetFamilyOutput
            {
                //Map Family Object to the GetFamilyOutput
            };
        }

注意:家族、DefaultMessage和FamilyConfiguration是在域层中创建的域对象。

你的意见是什么?

谢谢:)

编辑:注:我喜欢下面所有的答案,我用了一点:) (我不能把所有的答案标记为可接受的)。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2017-03-19 23:47:03

您的应用程序服务在#1中所做的工作是完全有效的:它用很少或根本没有业务逻辑知识来协调工作流。

然而,确实可以做的改进很少,例如:

  1. 我没有看到任何交易?电子邮件只应在成功完成交易后发送。
  2. 发送电子邮件可能被视为家庭认可的一种副作用。我想商业专家可能会说:“__,当家族获得批准,然后通过电子邮件通知感兴趣的各方”。因此,发布FamilyApproved域事件并在事件处理程序中移动电子邮件发送逻辑可能是明智的。 请注意,您希望只有在域事件被持久化到磁盘之后才异步调用处理程序,并且希望将事件保存在与聚合相同的事务中。
  3. 您可能还可以将邮件流程进一步抽象为类似于emailService.send(MessageTypes.FamilyApproved, family.getEmail())的内容。应用程序服务不必知道默认消息。
  4. 存储库通常只用于聚合根( AR ),如果DefaultMessage不是AR,那么我会考虑以不同的方式命名DefaultMessageRepository服务。

至于#2,虽然授权检查可以在域中进行,但更常见的做法是将域从此类任务中释放出来,并在应用层强制执行权限。您甚至可以拥有一个专用的标识&支持有限上下文(BC)的访问。

"//ProcessConfiguration将设置为空我无法显示的属性“

该解决方案不会很好(就像实现IFamilyProperty解决方案一样),因为您的域模型会被技术授权问题所污染。如果您想应用DDD,那么模型应该尽可能忠实于无处不在的语言(UL),我怀疑IFamilyProperty是您的领域专家会提到甚至理解的东西。允许属性成为null可能也会违反一些不变量。

这种解决方案的另一个问题是,域模型很少用于查询(它是为命令构建的),因此通常更倾向于完全绕过它,而倾向于直接访问DB。在域中实现授权将阻止您轻松地执行授权。

至少出于这些原因,我认为最好在域外实现授权检查。在那里,您可以自由地使用您想要的任何实现,并且适合您的需要。例如,我认为从DTO中去掉值可能是合法的。

票数 4
EN

Stack Overflow用户

发布于 2017-03-20 07:04:09

我还怀疑是否可以在应用程序服务中放置一些逻辑。但是一旦我读了弗拉基米尔·霍里科夫的“域服务与应用程序服务文章”,事情就变得更干净了。它说

域服务保持域逻辑,而应用程序服务不支持域逻辑。

并以很好的例子说明了这个想法。因此,在您的情况下,我认为将这些场景放置到Application中是完全可以的,因为它不包含域逻辑。

票数 2
EN

Stack Overflow用户

发布于 2017-03-19 21:40:24

第1章

我通常将这个逻辑移到域层--服务层()。

因此,应用程序层只需调用:

代码语言:javascript
复制
public ApproveFamilyOutput ApproveFamily(ApproveFamilyInput input)
{
    var approveService = diContainer.Get<ApproveService>(); // Or correctly injected by constructor
    var result = approveService.ApproveFamily(input);

    // Convert to ouput
}

域服务(AppproveService类)如下所示:

代码语言:javascript
复制
public ApproveResult ApproveFamily(ApproveFamilyInput input)
{
     var family = _familyRepository.GetFamily(input.Username);
     family.Approve();

     _familyRepository.Update(family);
     bool isSaved = _familyRepository.Save();

     if(isSaved)
         _eventPublisher.Publish(family.raisedEvents);
     // return result
}

为了使其工作(并遵循六边形/洋葱体系结构),域层定义其依赖项(IFamilyRepository、IDefaultMessageRepository等)的所有接口,应用层向域层注入特定的实现。

为了表明这一点:

  1. 域层是独立的
  2. 域对象是纯由实体、值对象组成的。
  3. 域对象不调用存储库,这取决于域服务
  4. 域对象引发事件。
  5. 不相关的逻辑由事件(事件处理程序)处理,例如发送电子邮件,它遵循开闭原则。
代码语言:javascript
复制
class FamilyApprovedHandler : IHandle<FamilyApprovedEvent>
{
    private readonly IDefaultMessageRepository _defaultMessageRepository;
    private readonly IEmailSender _emailSender;
    private readonly IEmailProvider _emailProvider;

    // ctor

    public Task Handle(FamilyApprovedEvent event)
    {
        var defaultMessage = _defaultMessageRepository.GetDefaultMessage(MessageTypes.FamilyApproved);

        var email = _emailProvider.Generate(event.Family, defaultMessage.Subject, defaultMessage.Message);

        _emailSender.Send(email);
    }
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42882053

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档