public class Member {
public string LoginName { get; set; } // The unique key
public int ReputationPoints { get; set; }
}
public class Item {
public int ItemID { get; private set; } // The unique key
public string Title { get; set; }
public string Description { get; set; }
public DateTime AuctionEndDate { get; set; }
public IList<Bid> Bids { get; set; }
}
public class Bid {
public Member Member { get; set; }
public DateTime DatePlaced { get; set; }
public decimal BidAmount { get; set; }
An aggregate entity groups together several domain model objects. There is aroot entitythat’s used
to identify the entire aggregate, and it acts as the “boss” for validation and persistence operations. The
aggregate is treated as a single unit with regard to data changes, so we need to create aggregates that
represent relationships that make sense in the context of the domain model, and create operations that
correspond logically to real business processes; that is, we need to create aggregates by grouping objects
that are changed as a group.
每一个聚合实体(的跟对象)只能有一个子对象,不能有多个对象,聚合的一个好处就是简化对象之间的关系
- DDD的作用
In general, aggregates add structure and accuracy to a domain model. They make it easier to apply
validation (the root entity becomes responsible for validating the state of all objects in the aggregate) and
are obvious units for persistence. And, because aggregates are essentially the atomic units of our domain
model, they are also suitable units for transaction management and cascade deletes from databases.
Persistence is not part of our domain model. It is an
independent or orthogonal concern in our separation of concerns pattern.
This means that we don’t want
to mix the code that handles persistence with the code that defines the domain model.
The usual way to enforce separation between the domain model and the persistence system is to
define repositories.
- 定义仓库
public class MembersRepository {
public void AddMember(Member member) { /* Implement me */ }
public Member FetchByLoginName(string loginName) { /* Implement me */ }
public void SubmitChanges() { /* Implement me */ }
}
public class ItemsRepository {
public void AddItem(Item item) { /* Implement me */ }
public Item FetchByID(int itemID) { /* Implement me */ }
public IList<Item> ListItems(int pageSize,int pageIndex) { /* Implement me */ }
public void SubmitChanges() { /* Implement me */ }
}
第七章还会讲到
- 下面是松耦合组件的介绍
In our ideal situation, each component knows nothing about any other component and deals with
other areas of the application only through abstract interfaces. This is known as loose coupling, and it
makes testing and modifying our application easier.
上面的方法存在的问题
public class PasswordResetHelper {
public void ResetPassword() {
IEmailSender mySender = new MyEmailSender();
...call interface methods to configure e-mail details...
mySender.SendEmail();
}
}
这样的话,把PasswoirdResetHelper和MyEmailSender联系起来了。
这样就有了相互关联。为了解决这个问题,引入下面的概念。
- 利用依赖注入(DI)
DI的一种方法是使用构造方法,其中构造方法的参数是实现IEmailSender接口的对象。像下面的写法:
public class PasswordResetHelper {
private IEmailSender emailSender;
public PasswordResetHelper(IEmailSender emailSenderParam) {
emailSender = emailSenderParam;
}
public void ResetPassword() {
...call interface methods to configure e-mail details...
emailSender.SendEmail();
}
}
我们的类不创建上面实现接口的参数类,这个类是在实例化PasswordResetHelper时才被创建和传到PasswordResetHelper类里面。还有一种设置注入。
- DIContainer
DI容器就是Unity.第六章还会提到。unity.codeplex.com
为了理解Unity,先来利用Ninject.。
单元测试和集成测试
单元测试用在算法和业务逻辑上面
集成测试可以模拟用户,然后可以检测出bug等,也称为回归测试。
Both kinds of testing can be extremely valuable in web applications. Unit tests, which are simple to
create and run, are brilliantly precise when you are working on algorithms, business logic, or other backend
infrastructure.
The value of integration testing is that it can model how a user will interact with the UI, and can
cover the entire technology stack that your application uses, including the web server and database.
Integration testing tends to be better at detecting new bugs that have arisen in old features; this is known
as regression testing.
以上是单元测试和集成测试的好处。
单元测试可以使用A/A/A,下面是例子:
[TestClass]
public class AdminControllerTest {
[TestMethod]
public void CanChangeLoginName() {
// Arrange (set up a scenario)
Member bob = new Member() { LoginName = "Bob" };
FakeMembersRepository repositoryParam = new FakeMembersRepository();
repositoryParam.Members.Add(bob);
AdminController target = new AdminController(repositoryParam);
string oldLoginParam = bob.LoginName;
string newLoginParam = "Anastasia";
// Act (attempt the operation)
target.ChangeLoginName(oldLoginParam, newLoginParam);
// Assert (verify the result)
Assert.AreEqual(newLoginParam, bob.LoginName);
Assert.IsTrue(repositoryParam.DidSubmitChanges);
}
private class FakeMembersRepository : IMembersRepository {
public List<Member> Members = new List<Member>();
public bool DidSubmitChanges = false;
public void AddMember(Member member) {
throw new NotImplementedException();
}
public Member FetchByLoginName(string loginName) {
return Members.First(m => m.LoginName == loginName);
}
public void SubmitChanges() {
DidSubmitChanges = true;
}
}
}