1

我正在构建一个 N-Tier MVC 4 应用程序,该应用程序使用一个抽象的 CommandProcessor 层来验证和执行我的 CommandModel 来实现 Repository 和 UnitOfWork 模式以及域驱动设计。

我现在开始学习Workflow Foundation,并添加了一个处理用户注册的工作流项目。在工作流结束时,如果一切顺利,工作流将执行 ApproveMembership CodeActivity:

public sealed class ApproveMembership : CodeActivity
{
    [RequiredArgument]
    public InArgument<string> UserEmail { get; set; }

    protected override void Execute(CodeActivityContext context)
    {
        IDatabaseFactory databaseFactory = new DatabaseFactory();
        IUnitOfWork unitOfWork = new UnitOfWork(databaseFactory);
        IUserRepository userRepository = new UserRepository(databaseFactory);

        string userEmail = UserEmail.Get(context);

        User user = userRepository.Get(u => u.Email == userEmail);

        if (user != null)
        {
            user.Activated = true;
            userRepository.Update(user);
            unitOfWork.Commit();
        }
    }
}

还有一个类似的 RemoveMembership 活动,如果用户从不验证,则在一定时间后删除用户。

我的问题是,在我的工作流程中处理这个问题有意义吗?或者是更好的方法在工作流中使用依赖注入来获得一个 CommandBus,它将处理我必须创建的 2 个新的、几乎相同的命令模型,以及它们的验证和提交处理程序?这是在我的 MVC AccountController 中完成的。

    public class DeleteUserCommand : ICommand
    {
        public int UserId { get; set; }
    }
    public class ApproveUserCommand : ICommand
    {
        public int UserId { get; set; }
    }

    public sealed class RemoveMembership : CodeActivity
    {
        public InArgument<string> UserEmail { get; set; }

        private readonly ICommandBus commandBus;
        private readonly IUserRepository userRepository;

        public RemoveMembership(ICommandBus commandBus, IUserRepository userRepository)
        {
            this.commandBus = commandBus;
            this.userRepository = userRepository;
        }

        protected override void Execute(CodeActivityContext context)
        {
            string userEmail = UserEmail.Get(context);
            User user = userRepository.Get(u => u.Email == userEmail);

            if (user != null)
            {
                var command = new DeleteUserCommand
                {
                    UserId = user.UserId
                };

                IEnumerable<ValidationResult> errors = commandBus.Validate(command);
                if (!errors.Any())
                {
                    commandBus.Submit(command);
                }
            }
        }
    }

显然这是更多的代码,但它是好的设计吗?

我觉得我在写这篇文章时回答了我自己的问题,我认为答案是肯定的。但还是想听听专业意见。

4

1 回答 1

2

我的问题是,在我的工作流程中处理这个问题有意义吗?

我会将您在 CodeActivity 中的代码封装到应用程序服务中,并让工作流引用该应用程序服务:

class UserApplicationService
{
    public void ApproveMembership(string userEmail) 
    { 
        var user = userRepository.Get(u => u.Email == userEmail);
        if (user != null)
        {
            user.Activated = true;
            userRepository.Update(user);
        }
    }
}


public sealed class ApproveMembership : CodeActivity
{
    [RequiredArgument]
    public InArgument<string> UserEmail { get; set; }

    protected override void Execute(CodeActivityContext context)
    {
        var userEmail = UserEmail.Get(context);
        this.userService.ApproveMembership(userEmail);
    }
}

这样做的动机是 WWF 是您架构中的一个基础设施组件,它可以很好地将基础设施与域逻辑分离。WWF 可以替换为 NServiceBus 之类的东西,您可以在其中引用相同的应用程序服务。有人会说这是YAGNI的例子,但我认为分离域代码的基础设施在代码质量和推理域的能力方面都非常有益。

我还建议您尽可能将工作单元代码推送到基础架构中,这样应用程序服务就不必Commit显式调用。

或者是更好的方法在工作流中使用依赖注入来获得一个 CommandBus,它将处理我必须创建的 2 个新的、几乎相同的命令模型,以及它们的验证和提交处理程序?

这似乎更符合我的建议,但是根据您的代码示例,我建议更进一步,将 CodeActivity 中的大部分逻辑提取到应用程序服务中,或者以您可以的方式构建应用程序在不需要访问存储库的情况下分派命令。

于 2012-12-18T20:15:25.730 回答