zoukankan      html  css  js  c++  java
  • 如何开始DDD(续)

    上一篇针对用户注册案例简单介绍了如何使用 DDD,接下来我将继续针对这个例子做一下补充。先将User模型丰富起来,因为目前看上去他和贫血模型还没有啥大的区别。

    首先还是由领域专家来说明业务,他提出了用户注册成功后需要完善个人信息,这些信息包括姓名、生日、手机号。还需要用户提供一些联系信息,如地址,邮编等。那么我们就可以根据业务定义方法了。昨天netfocus兄指正了loginid所产生的歧义,表示认同,所以今天一并修改了一下。

    public class AddressInfo
    {
        public AddressInfo(string province, string city, string address, string postcode)
        {
            this.Province = province;
            this.City = city;
            this.Address = address;
            this.Postcode = postcode;
        }
    
        public string Province { get; private set; }
        public string City { get; private set; }
        public string Address { get; private set; }
        public string Postcode { get; private set; }
    }
    
    public class User
    {
        public User(string name, string password, string email)
        {
            this.Name = name;
            this.Password = password;
            this.Email = email;
        }
    
        public string Id { get; private set; }
        public string Name { get; private set; }
        public string Password { get; private set; }
        public string RealName { get; private set; }
        public string Email { get; private set; }
        public string Cellphone { get; private set; }
        public string Birthday { get; private set; }
        public AddressInfo Address { get; private set; }
    
        public void UpdateBasicInfo(string realName, string birthday, string cellphone)
        {
            this.RealName = realName;
            this.Birthday = birthday;
            this.Cellphone = cellphone;
        }
    
        public void UpdateAddress(AddressInfo address)
        {
            this.Address = address;
        }
    }

    那么前端的代码也很简单

    public class UserController
    {
        private readonly IUserRepository _userRepository;
        public void SetProfile(FormCollection form)
        {
            var user = _userRepository.Get(form.Get("id"));
    
            user.UpdateBasicInfo(form.Get("name"), form.Get("birthday"), form.Get("cellphone"));
        }
    
        public void SetAddress(FormCollection form)
        {
            var user = _userRepository.Get(form.Get("id"));
    
            var address = new AddressInfo(form.Get("province"), form.Get("city"), 
                form.Get("address"), form.Get("postcode"));
    
            user.UpdateAddress(address);
        }
    }

    以上的代码很好理解,只是设计了一个AddressInfo的值对象。

    接下来将演示一下用户登录验证和修改密码。一般的做法:

    public interface IUserRepository
    {
        User GetByName(string loginId);
    }
    
    public class UserController
    {
        private readonly IUserRepository _userRepository;
        public UserController(IUserRepository userRepository)
        {
            this._userRepository = userRepository;
        }
    
        public void Logon(FormCollection form)
        {
            User user = _userRepository.GetByName(form.Get("LoginId"));
            if (user == null)
                throw new Exception("loginId", "账号不存在。");
            if (user.Password != form.Get("Password"))
                throw new Exception("password", "密码不正确。");
    
            FormsAuthentication.SetAuthCookie(user.Name, createPersistentCookie);
        }
    }

    请注意上述代码比较密码是错误的方式,因为上一篇说明了密码是加过密的。所以要修改一下,首先要注入IEncryptionService,那么就会这样判断

    if (user.Password != _encryptionService.Encrypt(form.Get("Password")))

    这样会有什么问题呢。目前IEncryptionService的接口相对还比较简单,如果IEncryptionService提供了针对不同业务的好多加密接口,那么前端人员就需要详细了解IEncryptionService的api,增加了复杂度。再对User封装一个方法,然后对Contoller代码再稍做修改

    public class User
    {
        public bool VerifyPassword(string password, IEncryptionService encryptionService)
        {
            return this.Pasword == encryptionService.Encrypt(password);
        }
    }
    
    public class UserController
    {
        public void Logon(FormCollection form)
        {
            User user = _userRepository.GetByName(form.Get("LoginId"));
            if (user == null)
                throw new Exception("loginId", "账号不存在。");
            if (user.VerifyPassword(form.Get("Password"), _encryptionService))
                throw new Exception("password", "密码不正确。");
    
            FormsAuthentication.SetAuthCookie(user.Name, createPersistentCookie);
        }
    }

    这样具体密码采用了什么加密接口就不用关心了,将此规则封闭在了domain内,还有一个主要目的是为了修改密码时能够复用。也许你并不认同这种做法,好像也没啥变化,当然也没关系,解决问题就行,我只想表达聚合可以封装哪些方法。
    再仔细考虑我觉得上述代码表达的业务还是比较多,首先要查询该登录名的用户是否存在,再去验证密码,如果需求再有其他规则,如禁用的用户不能登录,具有时效性的用户过期了也不能登录等等,这样是不是越来越复杂,前端开发人员需要掌握的业务知识就会越来越多,所以最好将此业务封装在领域内,ui端只需要传入登录名和密码。
    说了这么多,User本身是无法做到这一点的,那么还是要将这些业务规则写在上一篇提到过的DomainService

    public class DomainService
    {
        private readonly IUserRepository _userRepository;
        private readonly IEncryptionService _encryptionService;
        public DomainService(IUserRepository userRepository, IEncryptionService encryptionService)
        {
            this._userRepository = userRepository;
            this._encryptionService = encryptionService;
        }
    
        public User Authenticate(string loginId, string password)
        {
            var user = _userRepository.GetByName(loginId);
            if (user == null)
                throw new Exception("loginId", "账号不存在。");
            if (!user.VerifyPassword(password, _encryptionService))
                throw new Exception("password", "密码不正确。");
    
            return user;
        }
    }
    
    public class UserController
    {
        public void Logon(FormCollection form)
        {
            try {
                User user = _domainService.Authenticate(form.Get("LoginId"), form.Get("Password"));
                FormsAuthentication.SetAuthCookie(user.Name, createPersistentCookie);
            }
            catch (Exception) {
                throw;
            }
        }
    }

    这样是不是更好一点呢?
    接下来再来说修改密码。直接上代码吧

    public class User
    {
        public void ChangePassword(string oldPwd, string newPwd, IEncryptionService encryptionService)
        {
            if (!this.VerifyPassword(oldPwd, encryptionService))
                throw new Exception("旧密码输入不正确。");
    
            this.Pasword = encryptionService.Encrypt(newPwd);
        }
    }
    
    public class UserController
    {
        public void ModifyPassword(FormCollection form)
        {
            try {
                User user = _userRepository.GetByName(User.Identity.Name);
                user.ChangePassword(form.Get("oldPwd"), form.Get("newPwd"), _encryptionService);
                _userRepository.Update(user);
            }
            catch (Exception) {
                
                throw;
            }
        }
    }

    好吧,这到这里吧,希望对你有帮助。下一篇再继续讨论丰富一下用户注册的过程,引入事件驱动。

  • 相关阅读:
    052-247(新增70题2018)
    052-246(新增70题2018)
    052-245(新增70题2018)
    Java项目软件测试基础1
    hibernate基础02:封装hibernateUtil,实现CRUD
    hibernate基础01:创建hibernate Demo实例
    eclipse快捷键
    Java插入MySQL数据库中文乱码,步骤详解
    LINUX切换普通用户提示:命令行提示:-BASH-4.1$ ,问题解决
    Mysql如何让百万数据快速分页,提高查询效率
  • 原文地址:https://www.cnblogs.com/younghan/p/3891072.html
Copyright © 2011-2022 走看看