【ABP】项目示例(2)——聚合根和实体
聚合根和实体在上一章节中,已经完成了项目搭建的前置准备,在这一章节中,实现领域层的聚合根和实体
创建名称为General.Backend.Domain的标准类库,分别新建名称为Entities、Services、IRepositories和Specifications的文件夹,用于存放实体和聚合根、领域服务、仓储接口和规约。
本项目使用ABP相关的Nuget包的版本为8.3.0,为保持版本一致,后续其他ABP相关的Nuget包都使用该版本。
在程序包管理控制台选中General.Backend.Domain,执行以下命令安装ABP领域相关的Nuget包。
Install-Package Volo.Abp.Ddd.Domain -v 8.3.0新建名称为GeneralDomainModule的领域模块类
public class GeneralDomainModule : AbpModule{ public override void ConfigureServices(ServiceConfigurationContext context) { }}创建名称为General.Backend.Domain.Shared的标准类库,分别新建名称为Enums和Consts的文件夹,用于存放枚举和常量。
在程序包管理控制台选中General.Backend.Domain.Shared,执行以下命令安装ABP领域共享相关的Nuget包。
Install-Package Volo.Abp.Ddd.Domain.Shared -v 8.3.0新建名称为GeneralDomainSharedModule的领域共享模块类
public class GeneralDomainSharedModule : AbpModule{}General.Backend.Domain添加项目引用General.Backend.Domain.Shared
用户聚合
在Enums文件夹下新建名称为FrozenStatus的用户冻结状态枚举
/// <summary>/// 冻结状态/// </summary>public enum FrozenStatus{ /// <summary> /// 未冻结 /// </summary> UnFrozen = 1, /// <summary> /// 已冻结 /// </summary> Frozen = 2}在Consts文件夹下新建名称为UserConsts的用户常量类,定义用户领域使用到的常量,用于数据库表配置和常规校验
public static class UserConsts{ public const string UserTableName = "user"; public const string UserTableComment = "用户表"; public const string UserRoleTableName = "user_role"; public const string UserRoleTableComment = "用户角色表"; public const string AdminAccount = "admin"; public const string AdminName = "Admin"; public const int MaxLoginErrorCount = 5; public const int MinAccountLength = 2; public const int MaxAccountLength = 32; public const int MinPasswordLength = 6; public const int MaxPasswordLength = 32; public const int MinNameLength = 1; public const int MaxNameLength = 32; public const int MaxContactLength = 64; public const int MaxAddressLength = 64;}在Entities文件夹下新建名称为User的用户聚合根类和UserRole的用户角色实体类,业务逻辑为一个用户拥有一个或者多个角色,用户的角色需要通过用户聚合来管理
/// <summary>/// 用户/// </summary>public class User : FullAuditedAggregateRoot<Guid>{ /// <summary> /// 账号 /// </summary> public virtual string Account { get; private set; } = string.Empty; /// <summary> /// 密码 /// </summary> public virtual string Password { get; private set; } = string.Empty; /// <summary> /// 用户名称 /// </summary> public virtual string Name { get; private set; } = string.Empty; /// <summary> /// 联系方式 /// </summary> public virtual string? Contact { get; set; } /// <summary> /// 地址 /// </summary> public virtual string? Address { get; set; } /// <summary> /// 登录错误次数 /// </summary> public virtual byte LoginErrorCount { get; private set; } /// <summary> /// 是否冻结(1:未冻结,2:已冻结) /// </summary> public virtual FrozenStatus IsFrozen { get; private set; } /// <summary> /// 用户关联角色列表 /// </summary> public virtual ICollection<UserRole> UserRoles { get; private set; } = []; protected User() { } public User( Guid id, string account, string password, string name, string? contact = null, string? address = null) { Id = id; SetAccount(account); SetPassword(password); SetName(name); IsFrozen = FrozenStatus.UnFrozen; Contact = Check.Length(contact, nameof(contact), UserConsts.MaxContactLength); Address = Check.Length(address, nameof(address), UserConsts.MaxAddressLength); } private void SetAccount( string account) { Account = Check.Length(account, nameof(account), UserConsts.MaxAccountLength, UserConsts.MinAccountLength)!; } internal virtual void SetPassword( string password) { Password = Check.Length(password, nameof(password), UserConsts.MaxPasswordLength, UserConsts.MinPasswordLength)!; } public virtual void SetName( string name) { Name = Check.Length(name, nameof(name), UserConsts.MaxNameLength, UserConsts.MinNameLength)!; } internal virtual bool CheckPassword( string password) { Check.NotNullOrEmpty(password, nameof(password)); if (Password != password) { TryFrozen(); return false; } UnFrozen(); return true; } public virtual void UnFrozen() { LoginErrorCount = 0; IsFrozen = FrozenStatus.UnFrozen; } private bool TryFrozen() { var isSuccess = false; if (Account != UserConsts.AdminAccount) { LoginErrorCount += 1; if (LoginErrorCount >= UserConsts.MaxLoginErrorCount) { IsFrozen = FrozenStatus.Frozen; isSuccess = true; } } return isSuccess; } public virtual void SetRoles(ICollection<UserRole> userRoles) { UserRoles = userRoles; }}/// <summary>/// 用户角色/// </summary>public class UserRole : Entity<Guid>, IHasCreationTime{ /// <summary> /// 用户Id /// </summary> public virtual Guid UserId { get; private set; } /// <summary> /// 角色Id /// </summary> public virtual Guid RoleId { get; private set; } /// <summary> /// 创建时间 /// </summary> public virtual DateTime CreationTime { get; private set; } protected UserRole() { } internal UserRole( Guid id, Guid userId, Guid roleId) { Id = id; UserId = userId; RoleId = roleId; }}角色聚合
在Entities文件夹下新建名称为Role的角色聚合根类和RoleMenus的角色菜单实体类,业务逻辑为一个角色拥有一个或者多个菜单,用户拥有的菜单需要通过角色聚合来管理
/// <summary>/// 角色/// </summary>public class Role : FullAuditedAggregateRoot<Guid>{ /// <summary> /// 编码 /// </summary> public virtual string Code { get; private set; } = string.Empty; /// <summary> /// 名称 /// </summary> public virtual string Name { get; private set; } = string.Empty; /// <summary> /// 描述 /// </summary> public virtual string? Remark { get; set; } /// <summary> /// 角色关联菜单列表 /// </summary> public virtual ICollection<RoleMenu> RoleMenus { get; private set; } = []; protected Role() { } public Role( Guid id, string code, string name, string? remark = null) { Id = id; Code = Check.Length(code, nameof(code), RoleConsts.MaxCodeLength, RoleConsts.MinCodeLength)!; SetName(name); Remark = Check.Length(remark, nameof(remark), RoleConsts.MaxRemarkLength); } internal virtual void SetName( string name) { Name = Check.Length(name, nameof(name), UserConsts.MaxNameLength, UserConsts.MinNameLength)!; } public virtual void SetMenus(ICollection<RoleMenu> roleMenus) { RoleMenus = roleMenus; }}/// <summary>/// 角色菜单/// </summary>public class RoleMenu : Entity<Guid>, IHasCreationTime{ /// <summary> /// 角色Id /// </summary> public virtual Guid RoleId { get; private set; } /// <summary> /// 菜单编码 /// </summary> public virtual string MenuCode { get; private set; } = string.Empty; /// <summary> /// 创建时间 /// </summary> public virtual DateTime CreationTime { get; private set; } protected RoleMenu() { } internal RoleMenu( Guid id, Guid roleId, string menuCode) { Id = id; RoleId = roleId; MenuCode = Check.Length(menuCode, nameof(menuCode), RoleConsts.MaxMenuCodeLength, RoleConsts.MinMenuCodeLength)!; }}同样地在Consts文件夹下新建名称为RoleConsts的角色常量类,定义角色领域使用到的常量,用于数据库表配置和常规校验
public class RoleConsts{ public const string RoleTableName = "role"; public const string RoleTableComment = "角色表"; public const string RoleMenuTableName = "role_menu"; public const string RoleMenuTableComment = "角色菜单表"; public const string AdminRoleCode = "Admin"; public const string AdminRoleName = "Admin"; public const int MaxCodeLength = 32; public const int MinCodeLength = 1; public const int MaxNameLength = 32; public const int MinNameLength = 1; public const int MaxMenuCodeLength = 64; public const int MinMenuCodeLength = 1; public const int MaxRemarkLength = 255;}菜单聚合
在Entities文件夹下新建名称为Menu的菜单聚合根类
/// <summary>/// 菜单/// </summary>public class Menu : BasicAggregateRoot<Guid>, IHasCreationTime{ /// <summary> /// 编码 /// </summary> public virtual string Code { get; private set; } = string.Empty; /// <summary> /// 父编码 /// </summary> public virtual string ParentCode { get; private set; } = string.Empty; /// <summary> /// 名称 /// </summary> public virtual string Name { get; private set; } = string.Empty; /// <summary> /// 类型 /// </summary> public virtual string Type { get; private set; } = string.Empty; /// <summary> /// 层级 /// </summary> public virtual int Level { get; private set; } /// <summary> /// 图标 /// </summary> public virtual string? Icon { get; private set; } /// <summary> /// 路由地址 /// </summary> public virtual string? UrlAddress { get; private set; } /// <summary> /// 组件地址 /// </summary> public virtual string? ComponentAddress { get; private set; } /// <summary> /// 排序 /// </summary> public virtual int Sort { get; private set; } /// <summary> /// 创建时间 /// </summary> public virtual DateTime CreationTime { get; private set; } /// <summary> /// 子菜单 /// </summary> public List<Menu> SubMenu { get; private set; } = []; protected Menu() { } public Menu( string code, string parentCode, string name, string type, int level, int sort, string? icon = null, string? urlAddress = null, string? componentAddress = null) { Code = Check.Length(code, nameof(code), MenuConsts.MaxCodeLength, MenuConsts.MinCodeLength)!; ParentCode = Check.Length(parentCode, nameof(parentCode), MenuConsts.MaxParentCodeLength, MenuConsts.MinParentCodeLength)!; Name = Check.Length(name, nameof(name), MenuConsts.MaxNameLength, MenuConsts.MinNameLength)!; Type = Check.Length(type, nameof(type), MenuConsts.MaxTypeLength, MenuConsts.MinTypeLength)!; Level = level; Sort = sort; Icon = Check.Length(icon, nameof(icon), MenuConsts.MaxIconLength); UrlAddress = Check.Length(urlAddress, nameof(urlAddress), MenuConsts.MaxUrlAddressLength); ComponentAddress = Check.Length(componentAddress, nameof(componentAddress), MenuConsts.MaxComponentAddressLength); } private Menu( string code) { Code = Check.Length(code, nameof(code), MenuConsts.MaxCodeLength, MenuConsts.MinCodeLength)!; } internal static Menu BuildRoot() { return new Menu( MenuConsts.MenuRootCode); } internal static Menu BuildCatalog( string code, string name, string icon, string urlAddress, int sort) { return new Menu( code, MenuConsts.MenuRootCode, name, MenuConsts.CatalogTypeName, 1, sort, icon, urlAddress); } internal static Menu BuildMenu( string code, string parentCode, string name, string icon, string urlAddress, string componentAddress, int sort) { return new Menu( code, parentCode, name, MenuConsts.MenuTypeName, 2, sort, icon, urlAddress, componentAddress); } internal static Menu BuildFunc( string code, string parentCode, string name, int sort) { return new Menu( code, parentCode, name, MenuConsts.FuncTypeName, 3, sort); } public void SetSubMenu( List<Menu> subMenus) { Check.NotNull(subMenus, nameof(subMenus)); foreach (var parentCode in subMenus.Select(menu => menu.ParentCode)) { Check.Equals(Code, parentCode); } SubMenu = subMenus; }}同样地在Consts文件夹下新建名称为MenuConsts的菜单常量类
public class MenuConsts{ public const string MenuTableName = "menu"; public const string MenuTableComment = "菜单表"; public const string MenuRootCode = "root"; public const string CatalogTypeName = "C"; public const string MenuTypeName = "M"; public const string FuncTypeName = "F"; public const int MaxCodeLength = 64; public const int MinCodeLength = 1; public const int MaxParentCodeLength = 64; public const int MinParentCodeLength = 1; public const int MaxNameLength = 32; public const int MinNameLength = 1; public const int MaxTypeLength = 8; public const int MinTypeLength = 1; public const int MaxIconLength = 32; public const int MaxUrlAddressLength = 64; public const int MaxComponentAddressLength = 64;}上面已经对用户、角色和菜单领域创建了相应的聚合根,相关的业务逻辑以聚合根和实体中方法来实现
解决方案的目录结构现如下
在下一章节中,使用仓储作为领域模型和数据模型的桥梁,将领域模型持久化到数据库中的数据模型中
页:
[1]