Strongly typed Collections of Abstract Objects
Some of you know that we are in the process of not using Csla anymore, this is a decision that just seems logical for us, but that is another post. I need a little help implementing a problem in my domain. Now the sample I am posting is out of a dumbed down version I have been using to spike with. Take a look at the following class diagram and then I will explain what is going on and what my problem is:
What is going on here you ask? Order is the primary object type, but it is abstract. I have maybe 7 different types of orders, service order, make order, repair order, etc… each order has several attributes that are always present on every order. What makes each order unique is the attributes that are not shared between all orders. This is why I have decided to use inheritance.
One of the items that are common between all types of orders is that they all have many parts. However, I want the base Order class to have a collection of base OrderPart objects. OrderPart is the mapping object in between Order and Part for the Many To Many relationship, I am using an object because this association has state of it’s own. Now to complicate matters even more, each concrete Order can have a different concrete type of Part and OrderPart assigned to it.
I have the model all setup and Have written some tests against it as I wrote it, not really test first but hey, I am spiking here. So below is one of the tests that shows most of this in action.
public void CanCreateOrderWithCustomerAndParts()
{
var order = new CustomOrder();
order.Customer = new Customer
{
Name = "John Smith",
Address =
new Address
{
Street = "45623 Easy",
State = "TX",
City = "Dalals",
ZipCode = "73884"
}
};
order.OrderNumber = "Y984939";
order.Amount = (decimal) 1234.33;
order.CustomOrderDescription = "Testing adlkfjl 123";
order.Parts =
new List
{
new CustomOrderPart
{
Part =
new CustomPart
{
PartNumber = "Part456sdf34",
Description = "Test Partasd 87",
CustomPartText = "TEST",
},
Quantity = 12
},
new CustomOrderPart
{
Part =
new CustomPart
{
PartNumber = "PARTWITHNULLPULLLOCATION",
Description = "Test Part 848sasd8243",
CustomPartText = "TEST",
},
Quantity = 251
}
};
With.Transaction(() => Repository.Save(order));
Order createdOrder = Repository.Get(order.Id);
Assert.IsNotNull(createdOrder);
Assert.AreEqual(2, createdOrder.Parts.Count);
}
Don’t make fun of my test code, like I said, it’s a spike. But when I create a CustomOrder I am creating Parts as a new List<OrderPart> which is how it is implemented. But then I had new CustomOrderParts and CustomParts into that collection. NHibernate will persist this information correctly into the database and will retrieve it correclty from the DB. So seemingly all works fine right, sure I guess. Maybe it is just because I have been in Csla land so long that I don’t know any different but what I am really missing is being able for the concrete order object to know what concrete type of OrderPart is contained in the parts collection. As I wrote that I just kicked myself, if that isn’t a violation of Seperation of Concerns I don’t know what is, or is it? I mean Order or a concrete Order is basically an Aggregate Root in DDD terms so it is the responsible entry point into the chain/web of objects that make up the aggregate.
So, for my real question which I am not sure of now, How would you tackle this design problem? How do you handle child collections? Am I just way off the reservation?
Consider not having your IList exposed to the world. This is generally going to lead to other pains (besides the one you have demonstrated here).
In your case, consider having a protected IList and managing what goes in and comes out through public implementations on the individual derived classes.
I.e. IEnumerable GetOrderParts() on the “CustomOrder” class, and also void AddPartToOrder(CustomPart part, int quantity), etc.
-c
Chad,
Is an IEnumerable what I really want to use? It just seems so useless. I mean you can iterate the collection but that is it. Part of this is just my ignorance I know, because I am used to large list classes that do everything under the sun.
Also, is it okay for the protected IList to really be an “internal” ilist for mapping purposes?
I’m glad to hear the reason you are leaving from CLSA. Thanks.
BTW, do you twitter?
@Frank yes, you can get me on twitter as ryankelley
@Ryan:
If there is some reason you need some functionality on IList that isn’t on IEnumerable+LINQ, then go head, but make it a read-only IList or a copy list so that people aren’t mucking around with your list structure (adding/removing items, etc).
Though I would argue that, most likely, you won’t need IList functionality unless there is some 3rd party grid component or something that requires it.
If the worst comes, you could always to .ToArray() or ToList() on the enumerable.
Looking forward to the post on the reasons for dumping CSLA! As we haven’t started our project yet!!