ABP VNext 微服务搭建入门(3)-- 业务逻辑写在哪里

业务逻辑可以分为领域逻辑非领域逻辑。一般来说,领域逻辑包含新增修改,由领域驱动且不易变,非领域逻辑包含查询删除,由数据驱动且易变

一、领域逻辑

1、领域模型

单个实体内部的领域逻辑,不进行持久化,持久化交给上层处理,如领域服务,应用服务。

public class Product : AuditedAggregateRoot<Guid>
    {
        [NotNull]
        public string Code { get; private set; }

        [NotNull]
        public string Name { get; private set; }

        public float Price { get; private set; }

        public int StockCount { get; private set; }

        public string ImageName { get; private set; }

        private Product()
        {
            //Default constructor is needed for ORMs.
        }

        internal Product(
            Guid id,
            [NotNull] string code, 
            [NotNull] string name, 
            float price = 0.0f, 
            int stockCount = 0,
            string imageName = null)
        {
            Check.NotNullOrWhiteSpace(code, nameof(code));

            if (code.Length >= ProductConsts.MaxCodeLength)
            {
                throw new ArgumentException($"Product code can not be longer than {ProductConsts.MaxCodeLength}");
            }

            Id = id;
            Code = code;
            SetName(Check.NotNullOrWhiteSpace(name, nameof(name)));
            SetPrice(price);
            SetImageName(imageName);
            SetStockCountInternal(stockCount, triggerEvent: false);
        }

        public Product SetName([NotNull] string name)
        {
            Check.NotNullOrWhiteSpace(name, nameof(name));

            if (name.Length >= ProductConsts.MaxNameLength)
            {
                throw new ArgumentException($"Product name can not be longer than {ProductConsts.MaxNameLength}");
            }

            Name = name;
            return this;
        }
          ...
      }

2、领域服务

涉及一个或多个完整实体的领域逻辑。实现 DomainService

public class ProductManager : DomainService
{
    private readonly IRepository<Product, Guid> _productRepository;

    public ProductManager(IRepository<Product, Guid> productRepository)
    {
        _productRepository = productRepository;
    }

    public async Task<Product> CreateAsync(
        [NotNull] string code,
        [NotNull] string name,
        float price = 0.0f,
        int stockCount = 0)
    {
        var existingProduct = 
            await _productRepository.FirstOrDefaultAsync(p => p.Code == code);
            
        if (existingProduct != null)
        {
            throw new ProductCodeAlreadyExistsException(code);
        }

        return await _productRepository.InsertAsync(
            new Product(
                GuidGenerator.Create(),
                code,
                name,
                price,
                stockCount
            )
        );
    }
}

3、领域事件

通过事件的发布订阅来处理领域逻辑,对复杂的流程业务进行解耦,实现事务的最终一致性。领域事件可以分为本地事件和分布式事件。

public class Product : AuditedAggregateRoot<Guid>
    {
        ...

        public Product SetStockCount(int stockCount)
        {
            return SetStockCountInternal(stockCount);
        }

        private Product SetStockCountInternal(int stockCount, bool triggerEvent = true)
        {
            if (StockCount < 0)
            {
                throw new ArgumentException($"{nameof(stockCount)} can not be less than 0!");
            }

            if (StockCount == stockCount)
            {
                return this;
            }

            if (triggerEvent)
            {
                AddDistributedEvent(
                    new ProductStockCountChangedEto(
                        Id,
                        StockCount,
                        stockCount
                    )
                );
            }

            StockCount = stockCount;
            return this;
        }
     }

二、非领域逻辑

仓储

仓储用于保存和获取聚合对象。仓储分为两种,一种是基于集合的,一种是基于持久化的。Abp 的 IRespository 实现了常用的CRUD方法。
如果不够用(极少情况),可以自定义仓储实现IRespository。所有不含业务逻辑的CRUD都写在这里。代码实现写在基础架构层,接口定义在Domian层。

应用服务

负责调用领域逻辑增改和处理非领域逻辑的查删,并持久化。实现 ApplicationService。

[Authorize(ProductManagementPermissions.Products.Default)]
public class ProductAppService : ApplicationService, IProductAppService
{
	private readonly ProductManager _productManager;
	private readonly IRepository<Product, Guid> _productRepository;

	public ProductAppService(ProductManager productManager, IRepository<Product, Guid> productRepository)
	{
		_productManager = productManager;
		_productRepository = productRepository;
	}

	public async Task<PagedResultDto<ProductDto>> GetListPagedAsync(PagedAndSortedResultRequestDto input)
	{
		await NormalizeMaxResultCountAsync(input);

		var products = await _productRepository
			.OrderBy(input.Sorting ?? "Name")
			.Skip(input.SkipCount)
			.Take(input.MaxResultCount)
			.ToListAsync();

		var totalCount = await _productRepository.GetCountAsync();

		var dtos = ObjectMapper.Map<List<Product>, List<ProductDto>>(products);

		return new PagedResultDto<ProductDto>(totalCount, dtos);
	}

	public async Task<ListResultDto<ProductDto>> GetListAsync() //TODO: Why there are two GetList. GetListPagedAsync would be enough (rename it to GetList)!
	{
		var products = await _productRepository.GetListAsync();

		var productList =  ObjectMapper.Map<List<Product>, List<ProductDto>>(products);

		return new ListResultDto<ProductDto>(productList);
	}

	public async Task<ProductDto> GetAsync(Guid id)
	{
		var product = await _productRepository.GetAsync(id);

		return ObjectMapper.Map<Product, ProductDto>(product);
	}

	[Authorize(ProductManagementPermissions.Products.Create)]
	public async Task<ProductDto> CreateAsync(CreateProductDto input)
	{
		var product = await _productManager.CreateAsync(
			input.Code,
			input.Name,
			input.Price,
			input.StockCount,
			input.ImageName
		);

		return ObjectMapper.Map<Product, ProductDto>(product);
	}

	[Authorize(ProductManagementPermissions.Products.Update)]
	public async Task<ProductDto> UpdateAsync(Guid id, UpdateProductDto input)
	{
		var product = await _productRepository.GetAsync(id);

		product.SetName(input.Name);
		product.SetPrice(input.Price);
		product.SetStockCount(input.StockCount);
		product.SetImageName(input.ImageName);

		return ObjectMapper.Map<Product, ProductDto>(product);
	}

	[Authorize(ProductManagementPermissions.Products.Delete)]
	public async Task DeleteAsync(Guid id)
	{
		await _productRepository.DeleteAsync(id);
	}

	private async Task NormalizeMaxResultCountAsync(PagedAndSortedResultRequestDto input)
	{
		var maxPageSize = (await SettingProvider.GetOrNullAsync(ProductManagementSettings.MaxPageSize))?.To<int>();
		if (maxPageSize.HasValue && input.MaxResultCount > maxPageSize.Value)
		{
			input.MaxResultCount = maxPageSize.Value;
		}
	}
}
posted @ 2020-12-06 14:05  李锦成  阅读(793)  评论(0编辑  收藏  举报