本文忽略对abp vnext框架及DDD软件开发方法的解析,直入主题,讲解一个功能实现中各层代码的实现,步骤如下:
1、下载模块框架代码
通过使用 abp cli或者直接通过官方进行下载,如下图:
2、领域层定义
领域层划分为两个项目:
- Domain.Shared 包(项目) 命名为CompanyName.ModuleName.Domain.Shared,包含常量,枚举和其他类型, 它不能包含实体,存储库,域服务或任何其他业务对象. 可以安全地与模块中的所有层使用. 此包也可以与第三方客户端使用.
- Domain 包(项目) 命名为CompanyName.ModuleName.Domain, 包含实体, 仓储接口,领域服务接口及其实现和其他领域对象.
- Domain 包依赖于 Domain.Shared 包.
1)实体定义
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.MultiTenancy;
namespace demo1.Articles
{
/// <summary>
/// 文章
/// </summary>
public class Article : CreationAuditedEntity<Guid>, IMultiTenant
{
/// <summary>
/// 租户Id
/// </summary>
public Guid? TenantId { get; private set; }
/// <summary>
/// 标题
/// </summary>
/// <returns></returns>
[Required]
[MaxLength(100)]
public string Title { get; set; }
/// <summary>
/// 副标题
/// </summary>
/// <returns></returns>
[MaxLength(100)]
public string SubTitle { get; set; }
/// <summary>
/// 作者
/// </summary>
/// <returns></returns>
[MaxLength(50)]
public string Aauthor { get; set; }
/// <summary>
/// 内容
/// </summary>
/// <returns></returns>
[Required]
public string Content { get; set; }
/// <summary>
/// 来源
/// </summary>
public string Source { get; set; }
}
}
2)仓储接口
一般存放在与实体定义在同一个文件夹下,命名约定为:I{实体名}Repositor,如:IArticleRepository。
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using Volo.Abp.Domain.Repositories;
namespace demo1.Articles
{
/// <summary>
/// 文章 仓储接口
/// </summary>
public interface IArticleRepository : IRepository<Article, Guid>
{
Task<List<Article>> GetListAsync(
bool paged = true,
string sorting = null,
int maxResultCount = int.MaxValue,
int skipCount = 0,
string filter = null,
CancellationToken cancellationToken = default
);
Task<long> GetCountAsync(
string filter = null,
CancellationToken cancellationToken = default);
}
}
3、基础设施层的代码变动
- 推荐 为每个orm/数据库集成创建一个独立的集成包, 比如Entity Framework Core 和 MongoDB.
- 推荐 例如, 创建一个抽象Entity Framework Core集成的CompanyName.ModuleName.EntityFrameworkCore 包. ORM 集成包依赖于 Domain 包.
- 不推荐 依赖于orm/数据库集成包中的其他层.
- 推荐 为每个主要的库创建一个独立的集成包, 在不影响其他包的情况下可以被另一个库替换.
using demo1.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
namespace demo1.Articles
{
public class ArticleRepository : EfCoreRepository<Idemo1DbContext, Article, Guid>, IArticleRepository
{
public ArticleRepository(IDbContextProvider<Idemo1DbContext> dbContextProvider) : base(dbContextProvider)
{
}
public virtual async Task<List<Article>> GetListAsync(
bool paged = true,
string sorting = null,
int maxResultCount = int.MaxValue,
int skipCount = 0,
string filter = null,
CancellationToken cancellationToken = default
)
{
var query = (await GetDbSetAsync())
.WhereIf(
!filter.IsNullOrWhiteSpace(),
u => u.Title.Contains(filter)
)
.OrderBy(sorting.IsNullOrWhiteSpace() ? "CreationTime desc" : sorting);
if (paged)
{
return await query.PageBy(skipCount, maxResultCount).ToListAsync(GetCancellationToken(cancellationToken));
}
else
{
return await query.ToListAsync(GetCancellationToken(cancellationToken));
}
}
public virtual async Task<long> GetCountAsync(
string filter = null,
CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync())
.WhereIf(
!filter.IsNullOrWhiteSpace(),
u => u.Title.Contains(filter)
)
.LongCountAsync(GetCancellationToken(cancellationToken));
}
}
}
4、应用服务层的代码变动
将应用服务层划分为两个项目:
- Application.Contracts 包(项目) 命名为*CompanyName.ModuleName.Application.Contracts,包含应用服务接口和相关的数据传输对象(DTO).
- Application contract 包依赖于 Domain.Shared 包.
- Application 包(项目)命名为CompanyName.ModuleName.Application,包含应用服务实现.
- Application 包依赖于 Domain 包和 Application.Contracts 包.
5、数据库迁移
使用add-migration 和Update-Database进行数据库迁移。
总结:每个领域功能的开发流程大致如此。