最近准备入职,同事说过去参与一个C#的项目,蛮大的,然后我也开始深入接触。
PRO ASP.NET MVC
It’s time to rectify that, but before you go looking for the validation controls, remember
that this is an MVC application, and following the don’t-repeat-yourself principle, validation
is a model concern, not a UI concern. Validation often reflects business rules, which
are most maintainable when expressed coherently in one and only one place, not scattered
variously across multiple controller classes and ASPX and ASCX files. Also, by putting validation
right into the model, you ensure that its data integrity is always protected in the same
way, no matter what controller or view is connected to it. This is a more robust way of thinking
than is encouraged by WebForms-style <asp:XyzValidator> UI controls.
[b]
Aggregates and Simplification[/b]
Take another look at the auctions example diagram (Figure 3-4). As it stands, it doesn’t offer
much guidance when it comes to implementation with C# and SQL Server. If you load a member
into memory, should you also load all their bids, and all the items associated with those
bids, and all the other bids for those items, and all the members who have placed all those
other bids? When you delete something, how far does that deletion cascade through the object
graph? If you want to impose validation rules that involve relationships across objects, where
do you put those rules? And this is just a trivial example—how much more complicated will it
get in real life?
The DDD way to break down this complexity is to arrange domain entities into groups
called aggregates. Figure 3-5 shows how you might do it in the auctions example.
Figure 3-5. Auctions domain model with aggregates
Each aggregate has a root entity that defines the identity of the whole aggregate, and acts
as the “boss” of the aggregate for the purposes of validation and persistence. The aggregate is
a single unit when it comes to data changes, so choose aggregates that relate logically to real
business processes—that is, the sets of objects that tend to change as a group (thereby embedding
further insight into your domain model).
Objects outside a particular aggregate may only hold persistent references to the root
entity, not to any other object inside that aggregate (in fact, ID values for nonroot entities
don’t even have to be unique outside the scope of their aggregate). This rule reinforces
aggregates as atomic units, and ensures that changes inside an aggregate don’t cause data
corruption elsewhere.
In this example, “members” and “items” are both aggregate roots, because they have to
be independently accessible, whereas “bids” are only interesting within the context of an
item. Bids are allowed to hold a reference to members, but members can’t directly reference
bids because that would violate the item’s aggregate boundary. Keeping relationships onedirectional,
as much as possible, leads to considerable simplification of your domain model
and may well reflect additional insight into the domain. This might be an unfamiliar thought
if you’ve previously thought of a SQL database schema as being your domain model (given
that all relationships in a SQL database are bidirectional), but C# can model a wider range of
concepts.
A C# representation of our domain model so far looks like this:
PRO ASP.NET MVC
It’s time to rectify that, but before you go looking for the validation controls, remember
that this is an MVC application, and following the don’t-repeat-yourself principle, validation
is a model concern, not a UI concern. Validation often reflects business rules, which
are most maintainable when expressed coherently in one and only one place, not scattered
variously across multiple controller classes and ASPX and ASCX files. Also, by putting validation
right into the model, you ensure that its data integrity is always protected in the same
way, no matter what controller or view is connected to it. This is a more robust way of thinking
than is encouraged by WebForms-style <asp:XyzValidator> UI controls.
[b]
Aggregates and Simplification[/b]
Take another look at the auctions example diagram (Figure 3-4). As it stands, it doesn’t offer
much guidance when it comes to implementation with C# and SQL Server. If you load a member
into memory, should you also load all their bids, and all the items associated with those
bids, and all the other bids for those items, and all the members who have placed all those
other bids? When you delete something, how far does that deletion cascade through the object
graph? If you want to impose validation rules that involve relationships across objects, where
do you put those rules? And this is just a trivial example—how much more complicated will it
get in real life?
The DDD way to break down this complexity is to arrange domain entities into groups
called aggregates. Figure 3-5 shows how you might do it in the auctions example.
Figure 3-5. Auctions domain model with aggregates
Each aggregate has a root entity that defines the identity of the whole aggregate, and acts
as the “boss” of the aggregate for the purposes of validation and persistence. The aggregate is
a single unit when it comes to data changes, so choose aggregates that relate logically to real
business processes—that is, the sets of objects that tend to change as a group (thereby embedding
further insight into your domain model).
Objects outside a particular aggregate may only hold persistent references to the root
entity, not to any other object inside that aggregate (in fact, ID values for nonroot entities
don’t even have to be unique outside the scope of their aggregate). This rule reinforces
aggregates as atomic units, and ensures that changes inside an aggregate don’t cause data
corruption elsewhere.
In this example, “members” and “items” are both aggregate roots, because they have to
be independently accessible, whereas “bids” are only interesting within the context of an
item. Bids are allowed to hold a reference to members, but members can’t directly reference
bids because that would violate the item’s aggregate boundary. Keeping relationships onedirectional,
as much as possible, leads to considerable simplification of your domain model
and may well reflect additional insight into the domain. This might be an unfamiliar thought
if you’ve previously thought of a SQL database schema as being your domain model (given
that all relationships in a SQL database are bidirectional), but C# can model a wider range of
concepts.
A C# representation of our domain model so far looks like this: