業務邏輯可以分為領域邏輯和非領域邏輯。一般來說,領域邏輯包含新增和修改,由領域驅動且不易變,非領域邏輯包含查詢和刪除,由數據驅動且易變。
一、領域邏輯
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;
}
}
}