DDD 通过多层过滤器

DDD pass filters through multiple layers

在我的洋葱架构中,我有我的 PresentationLayer,它包含一个名为 LogRabbitMQFilters 的 class,具有不同的属性来过滤搜索。

我通过映射器在 ApplicationLayer 中传递 LogRabbitMQFilters :

    public RabbitMQController(IELKService iELKService, IMapper mapper)
    {
        _iELKService = iELKService;
        _mapper = mapper;
    }

    public async Task<IActionResult> Index(int? id, LogRabbitMQFilters filters)
    {
        var filtersMapped = _mapper.Map<LogRabbitMQFilters>(filters);

        var response = await _iELKService.GetLog(filtersMapped);

        /*some code.....*/
    }

在 ApplicationLayer 中,我将我的 logRabbitMQFilters 映射到在持久层中声明的 RabbitMQFilters,我这样调用我的存储库:

    public ELKService(IELKRepository iELKRepository, IMapper mapper)
    {
        _iELKRepository = iELKRepository;
        _mapper = mapper;
    }

    public async Task<LogResult> GetLog(LogRabbitMQFilters  logRabbitMQFilters)
    {
        var filterMapped = _mapper.Map<RabbitMQFilters>(logRabbitMQFilters);

        return await _iELKRepository.GetLogs(filterMapped); 
    }

这是最好的做法吗?还有另一种方法可以将我的过滤器 class 传递到存储库吗?我想到了规范模式,但我现在不知道它是否是一个好的解决方案。

In regards of complexity it always great to break up the application according to its responsibilities or concerns.

考虑到您的代码,您似乎遵循了行业指定的标准。为了更清楚起见,我还在此处添加了标准项目架构的示例代码片段。

Controller:

[Route("api/User")]
[ApiController]
public class UserController : ControllerBase
{
    private readonly IUserService _userService;

    public UserController(IUserService userService)
    {
        _userService = userService;
    }
    [HttpGet]
    [Authorize]
    [Route("users")]
    public async Task<ActionResult> Users()
    {
        var users = await _userService.GetAllUsers();
        return Ok(new ResponseViewModel { output = "success", msg = "request successful", returnvalue = users });
    }
    
}

Service Interface:

public interface IUserService
    {
        Task<List<UserViewModel>> GetAllUsers();
    }

Service Implementation:

public  class UserService : IUserService
    {
        private readonly IUserRepository _userRepository;

        public UserService(IUserRepository userRepository)
        {
            _userRepository = userRepository;
        }

        public async Task<List<UserViewModel>> GetAllUsers()
        {
            return await _userRepository.GetAllUsers();
        }
    }

Repository Interface:

public interface IUserRepository
    {
        Task<List<UserViewModel>> GetAllUsers();
    }

Repository Implementation:

public class UserRepository : IUserRepository
    {
        private readonly AppDbContext _dbContext;
        private readonly IMapper _mapper;
        public UserRepository(AppDbContext dbContext, IMapper mapper)
        {
            _dbContext = dbContext;
            _mapper = mapper;

        }

        public async Task<List<UserViewModel>> GetAllUsers()
        {
            var users = await _dbContext.Users.ToListAsync();
            var userViewModel = _mapper.Map<List<UserViewModel>>(users);
            return userViewModel;

        }
    }

Model:

public class UserViewModel
    {
        public long user_id { get; set; }
        public string full_name { get; set; }
    }

Note: Hope above steps guided you accordingly. Additionally, you could also have a look our official document for more information regarding industry practices.

DDD 和洋葱架构具有共同的原则(例如域隔离),但是它们在开发技术方面也有一些不同的方面。需要指出的是,架构和设计应该服务于我们的目标,而不是目标本身。

从你的描述来看,你似乎有一个 CRUD 风格的系统。我看不到任何业务规则、领域概念和规范。这当然不是坏事。

将域与其他层(表示、基础结构)隔离是有益的,尤其是当要应用复杂的验证并且要在复杂的实体对象中执行业务规则时。但是,在您的情况下,您将普通对象 LogRabbitMQFilters (表示层)映射到“自身”(应用层),然后映射到普通对象 RabbitMQFilters (基础结构层)。在像您这样的情况下(即使从 DDD/onion 的角度来看),将对象按原样从表示层传递到应用层是可以的,但是:

  1. 第二个映射不应该存在,因为基础设施层知道域,因此应该接收您的(大概)域实体 LogRabbitMQFilters
  2. LogRabbitMQFilters 实际上不是真正的域实体,因为如前所述,它不应​​用任何业务规则。
  3. 将对象从一层平面映射到另一层似乎毫无意义。

I thought of specification pattern but I don't now if it's a good solution

规范模式在我们想要将表达式打包为业务不变量时非常有用,例如有一个名为 ShortMessage(业务不变)的 class 封装了一个表达式,例如 Message.Length < 42。但是对于你的情况,我认为这没有用,因为你的应用程序的 CRUD 性质:你只是接收一些用户属性作为表示数据库 table 的 ORM 对象上下文中的操作数,以便做类似的东西:

myORMManager
    .FetchAll<MyTableObject>()
    .Filter(record =>
        record.DateDebut == filter.DateDebut &&
        record.DateFin == filter.DateFin &&
        .
        .
        record.Message == filter.Message
        .
        .
        .
    );

每个由 'and' 运算符分隔的谓词都可以视为规范,但是此类规范只是技术性的,因为它们不传达任何业务不变性。对象 filter 实际上可以是客户端请求。

总而言之,只要不考虑业务不变量(或至少复杂度较低),直接使用客户端属性作为数据库过滤器表达式的操作数来开发单层应用程序是可以接受的table ).如果您仍然想要一个 DDD 框架,即拥有一个应用程序服务(您可以在其中应用简单的验证,例如 DateFin > DateDebut)和一个存储库对象以及控制器对象,那么我建议使用一个 class“遍历”所有三个对象