怎么买网站空间,广告平面设计培训班学费一般多少,好看 大气的网站,怎样创建网站的代码博主最近失业在家#xff0c;找工作之余#xff0c;看了一些关于洋葱#xff08;整洁#xff09;架构的资料和项目#xff0c;有感而发#xff0c;自己动手写了个洋葱架构解决方案#xff0c;起名叫OnionArch。基于最新的.Net 7.0 RC1, 数据库采用PostgreSQL, 目前实现了…博主最近失业在家找工作之余看了一些关于洋葱整洁架构的资料和项目有感而发自己动手写了个洋葱架构解决方案起名叫OnionArch。基于最新的.Net 7.0 RC1, 数据库采用PostgreSQL, 目前实现了包括多租户在内的12个特性。该架构解决方案主要参考了NorthwindTraders sample-dotnet-core-cqrs-api 项目 B站上杨中科的课程代码以及博主的一些项目经验。洋葱架构的示意图如下一、OnionArch 解决方案说明解决方案截图如下可以看到该解决方案轻量化实现了洋葱架构每个层都只用一个项目表示。建议将该解决方案作为单个微服务使用不建议在领域层包含太多的领域根。源代码分为四个项目1. OnionArch.Domain- 核心领域层类库项目其主要职责实现每个领域内的业务逻辑。设计每个领域的实体Entity值对象、领域事件和领域服务在领域服务中封装业务逻辑为应用层服务。- 领域层也包含数据库仓储接口缓存接口、工作单元接口、基础实体、基础领域跟实体、数据分页实体的定义以及自定义异常等。2. OnionArch.Infrastructure- 基础架构层类库项目其主要职责是实现领域层定义的各种接口适配器Adapter。例如数据库仓储接口、工作单元接口和缓存接口以及领域层需要的其它系统集成接口。- 基础架构层也包含Entity Framework基础DbConext、ORM配置的定义和数据迁移记录。3. OnionArch.Application- 应用业务用例层类库项目其主要职责是通过调用领域层服务实现业务用例。一个业务用例通过调用一个或多个领域层服务实现。不建议在本层实现业务逻辑。- 应用业务用例层也包含业务用例实体Model、Model和Entity的映射关系定义业务实基础命令接口和查询接口的定义CQRS包含公共MediatR管道AOP处理和公共Handler的处理逻辑。4. OnionArch.GrpcService- 界面API层GRPC接口项目用于实现GRPC接口。通过MediatR特定业务用例实体Model消息来调用应用层的业务用例。- 界面API层也包含对领域层接口的实现例如通过HttpContext获取当前租户和账号登录信息。二、OnionArch已实现特性说明1.支持多租户通过租户字段基于Entity Framework实体过滤器和实现对租户数据的查询过滤protected override void OnModelCreating(ModelBuilder modelBuilder){
//加载配置
modelBuilder.ApplyConfigurationsFromAssembly(typeof(TDbContext).Assembly);//为每个继承BaseEntity实体增加租户过滤器
// Set BaseEntity rules to all loaded entity types
foreach (var entityType in GetBaseEntityTypes(modelBuilder))
{
var method SetGlobalQueryMethod.MakeGenericMethod(entityType);
method.Invoke(this, new object[] { modelBuilder, entityType });
}
}在BaseDbContext文件的SaveChanges之前对实体租户字段赋值//为每个继承BaseEntity的实体的Id主键和TenantId赋值
var baseEntities ChangeTracker.EntriesBaseEntity();
foreach (var entry in baseEntities)
{
switch (entry.State)
{
case EntityState.Added:
if (entry.Entity.Id Guid.Empty)
entry.Entity.Id Guid.NewGuid();
if (entry.Entity.TenantId Guid.Empty)
entry.Entity.TenantId _currentTenantService.TenantId;
break;
}
}多租户支持全部在底层实现包括租户字段的索引配置等。开发人员不用关心多租户部分的处理逻辑只关注业务领域逻辑也业务用例逻辑即可。2.通用仓储和缓存接口实现了泛型通用仓储接口批量更新和删除方法基于最新的Entity Framework 7.0 RC1为提高查询效率查询方法全部返回IQueryable包括分页查询方便和其它实体连接后再筛选查询字段。3.领域事件自动发布和保存在BaseDbContext文件的SaveChanges之前从实体中获取领域事件并发布领域事件和保存领域事件通知以备后查。//所有包含领域事件的领域跟实体
var haveEventEntities domainRootEntities.Where(x x.Entity.DomainEvents ! null x.Entity.DomainEvents.Any()).ToList();
//所有的领域事件
var domainEvents haveEventEntities
.SelectMany(x x.Entity.DomainEvents)
.ToList();
//根据领域事件生成领域事件通知
var domainEventNotifications new ListDomainEventNotification();
foreach (var domainEvent in domainEvents)
{
domainEventNotifications.Add(new DomainEventNotification(nowTime, _currentUserService.UserId, domainEvent.EventType, JsonConvert.SerializeObject(domainEvent)));
}
//清除所有领域根实体的领域事件
haveEventEntities
.ForEach(entity entity.Entity.ClearDomainEvents());
//生成领域事件任务并执行
var tasks domainEvents
.Select(async (domainEvent)
{
await _mediator.Publish(domainEvent);
});
await Task.WhenAll(tasks);
//保存领域事件通知到数据表中
DomainEventNotifications.AddRange(domainEventNotifications);领域事件发布和通知保存在底层实现。开发人员不用关心领域事件发布和保存逻辑只关注于领域事件的定义和处理即可。4.领域根实体审计信息自动记录在BaseDbContext文件的Savechanges之前对记录领域根实体的审计信息。//为每个继承AggregateRootEntity领域跟的实体的AddedBy,Added,LastModifiedBy,LastModified赋值//为删除的实体生成实体删除领域事件DateTime nowTime DateTime.UtcNow;
var domainRootEntities ChangeTracker.EntriesAggregateRootEntity();
foreach (var entry in domainRootEntities)
{
switch (entry.State)
{
case EntityState.Added:
entry.Entity.AddedBy _currentUserService.UserId;
entry.Entity.Added nowTime;
break;
case EntityState.Modified:
entry.Entity.LastModifiedBy _currentUserService.UserId;
entry.Entity.LastModified nowTime;
break;
case EntityState.Deleted:
EntityDeletedDomainEvent entityDeletedDomainEvent new EntityDeletedDomainEvent(
_currentUserService.UserId,
entry.Entity.GetType().Name,
entry.Entity.Id,
JsonConvert.SerializeObject(entry.Entity)
);
entry.Entity.AddDomainEvent(entityDeletedDomainEvent);
break;
}
}领域根实体审计信息记录在底层实现。开发人员不用关心审计字段的处理逻辑。5. 回收站式软删除采用回收站式软删除而不采用删除字段的软删除方式是为了避免垃圾数据和多次删除造成的唯一索引问题。自动生成和发布实体删除的领域事件代码如上。通过MediatR Handler接收实体删除领域事件将已删除的实体保存到回收站中。public class EntityDeletedDomainEventHandler : INotificationHandlerEntityDeletedDomainEvent
{
private readonly RecycleDomainService _domainEventService;public EntityDeletedDomainEventHandler(RecycleDomainService domainEventService)
{
_domainEventService domainEventService;
}public async Task Handle(EntityDeletedDomainEvent notification, CancellationToken cancellationToken)
{
var eventData JsonSerializer.Serialize(notification);
RecycledEntity entity new RecycledEntity(notification.OccurredOn, notification.OccurredBy, notification.EntityType, notification.EntityId, notification.EntityData);
await _domainEventService.AddRecycledEntity(entity);
}
}6.CQRS(命令查询分离)通过MediatR IRequest 实现了ICommand接口和Iquery接口业务用例请求命令或者查询继承该接口即可。public interface ICommand : IRequest
{
}public interface ICommandout TResult : IRequestTResult
{
}
public interface IQueryout TResult : IRequestTResult
{}
public class AddCategoryCommand : ICommand
{
public AddCategoryRequest Model { get; set; }
}代码中的AddCategoryCommand 增加类别命令继承ICommand。7.自动工作单元Commit通过MediatR 管道实现了业务Command用例完成后自动Commit开发人员不需要手动提交。public class UnitOfWorkProcessorTRequest, TResponse : IRequestPostProcessorTRequest, TResponse where TRequest : IRequestTResponse
{
private readonly IUnitOfWork _unitOfWork;public UnitOfWorkProcessor(IUnitOfWork unitOfWork)
{
_unitOfWork unitOfWork;
}
public async Task Process(TRequest request, TResponse response, CancellationToken cancellationToken)
{
if (request is ICommand || request is ICommandTResponse)
{
await _unitOfWork.CommitAsync();
}
}
}8.GRPC Message做为业务用例实体通过将GRPC proto文件放入Application项目重用其生成的message作为业务用例实体Model。public class AddCategoryCommand : ICommand
{
public AddCategoryRequest Model { get; set; }
}其中AddCategoryRequest 为proto生成的message。9.通用CURD业务用例在应用层分别实现了CURD的Command增改删和Query查询 Handler。开发人员只需要在GRPC层简单调用即可实现CURD业务。public async override TaskAddProductReply AddProduct(AddProductRequest request, ServerCallContext context)
{
CUDCommandAddProductRequest addProductCommand new CUDCommandAddProductRequest();
addProductCommand.Id Guid.NewGuid();
addProductCommand.Model request;
addProductCommand.Operation C;
await _mediator.Send(addProductCommand);
return new AddProductReply()
{
Message Add Product sucess
};
}10. 业务实体验证通过FluentValidation和MediatR 管道实现业务实体自动验证并自动抛出自定义异常。开发人员只需要定义验证规则即可public class AddCategoryCommandValidator : AbstractValidatorAddCategoryCommand
{
public AddCategoryCommandValidator()
{
RuleFor(x x.Model.CategoryName).NotEmpty().WithMessage(p 类别名称不能为空.);
}
}11.请求日志和性能日志记录基于MediatR 管道实现请求日志和性能日志记录。12. 全局异常捕获记录基于MediatR 异常接口实现异常捕获。三、相关技术如下* .NET Core 7.0 RC1* ASP.NET Core 7.0 RC1* Entity Framework Core 7.0 RC1* MediatR 10.0.1* Npgsql.EntityFrameworkCore.PostgreSQL 7.0.0-rc.1* Newtonsoft.Json 13.0.1* Mapster 7.4.0-pre03* FluentValidation.AspNetCore 11.2.2* GRPC.Core 2.46.5四、 找工作博主有10年以上的软件技术实施经验Tech Leader专注于软件架构设计、软件开发和构建专注于微服务和云原生K8s架构 .Net Core\Java开发和Devops。博主有10年以上的软件交付管理经验Project ManagerProduct Ower专注于敏捷Scrum项目管理、软件产品业务分析和原型设计。博主能熟练配置和使用 Microsoft Azure 和Microsoft 365 云平台获得相关微软认证和证书。我家在广州也可以去深圳工作。做架构和项目管理都可以希望能从事稳定行业的业务数字化转型。有工作机会推荐的朋友可以加我微信 15920128707微信名字叫Jerry.