上一篇针对用户注册案例简单介绍了如何使用 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;
}
}
}

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

如何开始DDD(续)的更多相关文章

  1. 用CQRS+ES实现DDD

    用CQRS+ES实现DDD 这篇文章应该算是对前三篇的一个补充,在写之前说个题外话,有园友评论这是在用三层架构在写DDD,我的个人理解DDD是一种设计思想,跟具体用什么架构应该没有什么关系,DDD也需 ...

  2. 开始DDD

    如何开始DDD(完) 连续写了两篇文章,这一篇我想是序的完结篇了.结合用户注册的例子再将他简单丰富一下.在这里只添加一个简单需求,就是用户注册成功后给用户发一封邮件.补充一下之前的代码 public ...

  3. DDD中的Unitwork与DomainEvent如何相容?(续)

    上篇中说到了面临的问题(传送门:DDD设计中的Unitwork与DomainEvent如何相容?),和当时实现的一个解决方案.在实际使用了几天后,有了新的思路,和@trunks 兄提出的观点类似.下面 ...

  4. DDD 领域驱动设计-三个问题思考实体和值对象(续)

    上一篇:DDD 领域驱动设计-三个问题思考实体和值对象 说实话,整理现在这一篇博文的想法,在上一篇发布出来的时候就有了,但到现在才动起笔来,而且写之前又反复读了上一篇博文的内容及评论,然后去收集资料, ...

  5. DDD~基础设施层~续

    回到目录 在之前写的DDD~基础设施层文章中,提到了UnitOfWork,它里面有一些方法,但经过项目证明,不应该有Save和IsExplicitSubmit,而这个工作单元只起到了数据上下文统一的作 ...

  6. 基于DDD的现代ASP.NET开发框架--ABP系列文章总目录

    ABP相关岗位招聘:给热爱.NET新技术和ABP框架的朋友带来一个高薪的工作机会 ABP交流会录像视频:ABP架构设计交流群-7月18日上海线下交流会的内容分享(有高清录像视频的链接) 代码自动生成: ...

  7. 我的“第一次”,就这样没了:DDD(领域驱动设计)理论结合实践

    写在前面 插一句:本人超爱落网-<平凡的世界>这一期,分享给大家. 阅读目录: 关于DDD 前期分析 框架搭建 代码实现 开源-发布 后记 第一次听你,清风吹送,田野短笛:第一次看你,半弯 ...

  8. 我也来说说DDD~大话目录

    回到占占推荐博客索引 DDD之前没有接触过,但一但有了接触就一发不可收拾,他会带去进入一个全新的世界! DDD不是新技术,而是新思想,新模式,是软件开发领域的一次突破,它更接近于业务,对于业务的改动它 ...

  9. DDD 领域驱动设计-“臆想”中的实体和值对象

    其他博文: DDD 领域驱动设计-三个问题思考实体和值对象 DDD 领域驱动设计-三个问题思考实体和值对象(续) 以下内容属于博主"臆想",如有不当,请别当真. 扯淡开始: 诺兰的 ...

随机推荐

  1. Python类继承(转发)

    目录 一.概述 二.类的继承 2.1 继承的定义 2.2 构造函数的继承 2.3 子类对父类方法的重写 三.类继承的事例 回到顶部 一.概述 面向对象编程 (OOP) 语言的一个主要功能就是“继承”. ...

  2. 深拷贝 浅拷贝 python

    1. copy.copy 浅拷贝 只拷贝父对象,不会拷贝对象的内部的子对象. 2. copy.deepcopy 深拷贝 拷贝对象及其子对象 一个很好的例子: # -*-coding:utf-8 -*- ...

  3. 微信小程序之 -----事件

    事件分类      1. 冒泡事件:     当一个组件上的事件被触发后,该事件会向父节点传递.      2. 非冒泡事件:   当一个组件上的事件被触发后,该事件不会向父节点传递.   常见的冒泡 ...

  4. python基础之Day7part1集合

    一.集合 1.定义 s=set() 2.特点 每个元素必须是不可变类型,但集合本身是可变类型的,有add和remove等功能 3.用途 去重(原理:for循环if判断元素是否已存在,不存在则追加) 关 ...

  5. Eclipse 中打开 python 交互窗口

  6. boost asio 学习(三)post与dispatch

    http://www.gamedev.net/blog/950/entry-2249317-a-guide-to-getting-started-with-boostasio?pg=4 本章节为io_ ...

  7. 【APP测试(Android)】--升级更新

  8. 《C#从现象到本质》读书笔记(八)第10章反射

    <C#从现象到本质>读书笔记(八)第10章反射 个人感觉,反射其实就是为了能够在程序运行期间动态的加载一个外部的DLL集合,然后通过某种办法找到这个DLL集合中的某个空间下的某个类的某个成 ...

  9. poj1860

    刚上来一堆英文着实有点蒙逼,仔细分析是一个Bellman的变形,只要能找出一个无限增大的环这个题就好解决了,我这里用的SPFA,用邻接链表进行储存,直接套用的模板,部分变量名字没有改的很好 #incl ...

  10. 1.5eigen中高级初始化

    1.5 高级初始化 这一节讨论一些初始化矩阵的高级方法. 1.The comma initializer eigen提供一个简单设定所有矩阵系数.向量.数组系数的初始化语法,从左上角开始到右下角结束. ...