其他分享
首页 > 其他分享> > Web应用开发教程 - Part 6: 作家:领域层

Web应用开发教程 - Part 6: 作家:领域层

作者:互联网

Web应用开发教程 - Part 6: 作家:领域层

//[doc-params]
{
    "UI": ["MVC","Blazor","BlazorServer","NG"],
    "DB": ["EF","Mongo"]
}

关于本教程

在这个教程系列中,你将创建一个基于ABP的Web应用程序,叫 Acme.BookStore。这个应用程序是用来管理书籍及其作者的列表的。它是使用以下技术开发:

本教程由以下几个部分组成:

下载源代码

本教程根据你对UIDatabase的偏好有多个版本。我们准备了几个组合的源代码供大家下载:

如果你在Windows上遇到 "文件名太长 "或 "解压错误",它可能与Windows的最大文件路径限制有关。Windows有一个最大的文件路径限制,即250个字符。要解决这个问题,在Windows 10中启用长路径选项

如果你遇到与Git有关的长路径错误,可以尝试用以下命令在Windows中启用长路径。见 https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path
git config --system core.longpaths true

简介

在前面的章节中,我们已经使用ABP的基础框架轻松地构建了以下服务:

对于 "作家 "部分:

开发工作将逐层展开,以便在一个时间段内集中开发一个单独的层。在一个真实的项目中,你将像前几部分那样,逐个开发你的应用程序的功能(垂直)。通过这种方式,你将体验两种方法。

实体作者

Acme.BookStore.Domain项目中创建一个Authors文件夹(命名空间),并在其中添加一个Author类:

using System;
using JetBrains.Annotations;
using Volo.Abp;
using Volo.Abp.Domain.Entities.Auditing;

namespace Acme.BookStore.Authors
{
    public class Author : FullAuditedAggregateRoot<Guid>
    {
        public string Name { get; private set; }
        public DateTime BirthDate { get; set; }
        public string ShortBio { get; set; }

        private Author()
        {
            /* This constructor is for deserialization / ORM purpose */
        }

        internal Author(
            Guid id,
            [NotNull] string name,
            DateTime birthDate,
            [CanBeNull] string shortBio = null)
            : base(id)
        {
            SetName(name);
            BirthDate = birthDate;
            ShortBio = shortBio;
        }

        internal Author ChangeName([NotNull] string name)
        {
            SetName(name);
            return this;
        }

        private void SetName([NotNull] string name)
        {
            Name = Check.NotNullOrWhiteSpace(
                name, 
                nameof(name), 
                maxLength: AuthorConsts.MaxNameLength
            );
        }
    }
}

AuthorConsts是一个简单的类,位于Acme.BookStore.Domain.Shared项目的Authors命名空间(文件夹)中。

namespace Acme.BookStore.Authors
{
    public static class AuthorConsts
    {
        public const int MaxNameLength = 64;
    }
}

Acme.BookStore.Domain.Shared项目中创建了这个类,因为我们以后会在数据传输对象 (DTOs)上重用它。

领域服务AuthorManager

Author构造函数和ChangeName方法是internal,所以它们只能在领域层使用。在Acme.BookStore.Domain项目的Authors文件夹(命名空间)中创建一个AuthorManager类:

using System;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Volo.Abp;
using Volo.Abp.Domain.Services;

namespace Acme.BookStore.Authors
{
    public class AuthorManager : DomainService
    {
        private readonly IAuthorRepository _authorRepository;

        public AuthorManager(IAuthorRepository authorRepository)
        {
            _authorRepository = authorRepository;
        }

        public async Task<Author> CreateAsync(
            [NotNull] string name,
            DateTime birthDate,
            [CanBeNull] string shortBio = null)
        {
            Check.NotNullOrWhiteSpace(name, nameof(name));

            var existingAuthor = await _authorRepository.FindByNameAsync(name);
            if (existingAuthor != null)
            {
                throw new AuthorAlreadyExistsException(name);
            }

            return new Author(
                GuidGenerator.Create(),
                name,
                birthDate,
                shortBio
            );
        }

        public async Task ChangeNameAsync(
            [NotNull] Author author,
            [NotNull] string newName)
        {
            Check.NotNull(author, nameof(author));
            Check.NotNullOrWhiteSpace(newName, nameof(newName));

            var existingAuthor = await _authorRepository.FindByNameAsync(newName);
            if (existingAuthor != null && existingAuthor.Id != author.Id)
            {
                throw new AuthorAlreadyExistsException(newName);
            }

            author.ChangeName(newName);
        }
    }
}

DDD提示:不要引入领域服务方法,除非它们确实需要并执行一些核心业务规则。在这里,我们需要这个服务能够强制执行名称唯一的约束。

这两个方法都会检查是否已经有一个给定名字的作家并抛出一个指定的业务异常,AuthorAlreadyExistsException定义在Acme.BookStore.Domain项目中(在Authors文件夹中),如下所示:

using Volo.Abp;

namespace Acme.BookStore.Authors
{
    public class AuthorAlreadyExistsException : BusinessException
    {
        public AuthorAlreadyExistsException(string name)
            : base(BookStoreDomainErrorCodes.AuthorAlreadyExists)
        {
            WithData("name", name);
        }
    }
}

BusinessException是一种特定的异常类型。当需要时抛出与领域相关的异常是一种很好的做法。它由ABP框架自动处理,可以很容易地进行本地化。WithData(...)方法用于向异常对象提供额外的数据,这些数据将在以后用于本地化消息或其他用途。

打开 Acme.BookStore.Domain.Shared 项目中的BookStoreDomainErrorCodes,并做如下修改:

namespace Acme.BookStore
{
    public static class BookStoreDomainErrorCodes
    {
        public const string AuthorAlreadyExists = "BookStore:00001";
    }
}

这是一个特定的字符串代表你的应用程序抛出的错误代码,可以由客户端应用程序处理。对于用户来说,你可能想把它本地化。打开Acme.BookStore.Domain.Shared项目里面的Localization/BookStore/en.json,并添加以下条目:

"BookStore:00001": "There is already an author with the same name: {name}"

每当你抛出一个AuthorAlreadyExistsException时,终端用户会在用户界面上看到一个漂亮的错误信息。

IAuthorRepository

AuthorManager注入了IAuthorRepository,所以我们需要定义它。在Acme.BookStore.Domain项目的Authors文件夹(命名空间)中创建这个新接口:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;

namespace Acme.BookStore.Authors
{
    public interface IAuthorRepository : IRepository<Author, Guid>
    {
        Task<Author> FindByNameAsync(string name);

        Task<List<Author>> GetListAsync(
            int skipCount,
            int maxResultCount,
            string sorting,
            string filter = null
        );
    }
}

我们将在下一部分实现这个存储库。

这两种方法可能看起来没有必要,因为标准资源库已存在IQueryable,你可以直接使用它们,而不是定义这样的自定义方法。你是对的,在一个真正的应用程序中就像那样做。然而,对于这个学习教程来说,解释如何在你真正需要的时候创建自定义的资源库方法是很有用的。

总结

This part covered the domain layer of the authors functionality of the book store application. The main files created/updated in this part was highlighted in the picture below:

这一部分涵盖了书店应用程序的作家管理功能的领域层。这一部分创建/更新的主要文件在下面的图片中高亮显示:
image

下篇

参见本教程的下一部分

标签:Web,教程,Part,Acme,BookStore,using,public,string,name
来源: https://www.cnblogs.com/tjubuntu/p/15713916.html