These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 posts ] 
Author Message
 Post subject: Repositories, DDD, and Collection Questions
PostPosted: Sun Oct 28, 2007 11:03 am 
Beginner
Beginner

Joined: Mon Nov 07, 2005 11:06 pm
Posts: 28
I am building a web app and am trying to follow the "aggregate" and "repository" design principle from Eric Evan's Domain Driven Design book. I am also using NHibernate for persistence.

I am finding myself having to create repositories for every kind of object in my system regardless of whether it is an aggregate root or not. Mainly this is because of the web application strategy I am used to using, where you basically supply an ID of the item you are working with, and then performing the operation based on that ID. For example...

Code:
editaddress.rails?id=123, deleteaddress.rails?id=123, etc.

So, even if Address is a part of the larger Customer aggregate, I end up needing repository methods like this to work with addresses...

Code:
Address address = AddressRepository.Load(id);
AddressRepository.Delete(address);

I would like to follow the aggregate/repository principles more closely, but I am having some challenges. One approach I am looking at is changing my design a bit to allow for the following...

Code:
deleteaddress.rails?customerid=222&addressid=123

And in my page, doing something more along the lines of...

Code:
Customer customer = CustomerRepository.Load(customerid);
customer.RemoveAddress(addressid);
CustomerRepository.Save(customer);

This seems better. I do not have to have repository methods for addresses. But, now I run into another issue. How to handle the removal of the address based on the id. In my Customer.RemoveAddress(int id) method, I end up with something like...

Code:
foreach (Address address in this.Addresses)
    if (address.ID == id)
        this.Addresses.Remove(address);

This does not seem like the best approach (with the looping through each address). I have gotten into a habit of using IList<Entity> collections to store my associated collections of objects, such as Addresses. Is there a better approach to allow a more "clean" and "explicit" approach to working with the addresses in my exampe?

Thanks for any suggestions!


Top
 Profile  
 
 Post subject:
PostPosted: Sun Oct 28, 2007 1:19 pm 
Newbie

Joined: Sat Oct 27, 2007 10:10 pm
Posts: 3
My suggestion isn't really well thought out, but it might help lead to one.

1)
It seemed like the addressID was the unique identifier/PK. This would lead me to believe that your original code was the right way to go about it. Because, NHibernate should automatically update any referencing customer objects associated with that session when the object is removed/altered. Which leaves the aggregate associations to be handled by NHibernate and not by your code (which is a good thing).

kevinwarner wrote:
Code:
Address address = AddressRepository.Load(id);
AddressRepository.Delete(address);


The reason I like this approach is because Address has a primary/unique identifier. Searching through an aggregrate only seems necessary when the aggregrate uses a composite (multi-part) id. For example, if your Address table's PK was CustomerID and AddressTypeID. Then you would need both pieces of information to pull up the appropriate entry. But, you are not constrained by the aggregate associate. And, are now free to interact directly with the Address without knowledge of the customer. The best part being, that NHibernate will keep track of the association with the Customer for you.


2)
However, if you want to keep the aggregrate association described within the code then you can try using a descriptive HQL search. Which is very similar to the Domain specific code you wrote. Of course, this puts the NHibernate code into your Domain model, which it looks like you were trying to avoid. If that's the case ...


3)
You might try using Spring.NET with NHibernate. Spring.NET ships with a class which is used to abstract the NHibernate code from the Domain Model, HibernateTemplate (http://www.springframework.org/docs/api/org/springframework/orm/hibernate/HibernateTemplate.html). I believe this class will also keep you from having to write somewhat repetitive DAO/Repository code. The problem with this, is that you have to learn another API and technology.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Oct 28, 2007 4:38 pm 
Senior
Senior

Joined: Mon Aug 21, 2006 9:18 am
Posts: 179
Long answer: My first question would be whether Address is a conceptual Value Object or not. IN other words, does it matter whether it matters which Address instance you are dealing with at any point in the application. Often times, address is only an Value Object (as defined in the DDD book).
Now, if you have an Address-as-Value-Object then really you can map it as a
Component within the NHibernate mapping and override the Hash/equals methods for Value comparison (not referential/identity comparison). Deleting an address would just be [customer.DeleteAddress(Address address)] and since you overrode equals method you can employ builtin .NET collection functions for removal which will NULL their values if it is a Component.

However,if you are concerned about WHICH Address instance and not just its values, then you have another aggregate and a reference to that aggregate from Customer. This would make having a repository for Address valid.

Even if you are mapping multiple Address objects to another table and have determined they are Value Objects, remember that you should enforce access to the Aggregate variants only through the Aggregate. The fact that Address requires an identifier property for the underlying persistence mechanism is just one of those pragmatic compromises we make and I'd still enforce deletion within the Aggregate as though it were not there while NHibernate handles the 1-to-many relational aspect.

_________________
If this helped...please remember to rate it!


Top
 Profile  
 
 Post subject:
PostPosted: Sun Oct 28, 2007 4:59 pm 
Expert
Expert

Joined: Tue Aug 23, 2005 5:52 am
Posts: 335
Wouldn't it be feasible to add a DeleteAddressById method to the Customer repository and pass it the Id of the address? By my understanding the conceptual value of a repository over a DAO is that it's designed to represent data functionality directly related to the aggregate root. I also believe that a repository is not strictly limited to accepting aggregate root ids or returning aggregate root object.

If you are dealing with addresses that correspond to Customer entities then your function on theCustomer repository should contain a delete query which will find addresses by id where they are also joined to a Customer - if no Customer exists with the specified id then it's not valid to delete through this method.

I don't know if I've got the wrong end of the stick or not so I might not actually be helping. If anyone else out there can clarify I'd appreciate it too.

Cheers,

Symon.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Oct 28, 2007 9:52 pm 
Beginner
Beginner

Joined: Mon Nov 07, 2005 11:06 pm
Posts: 28
Thanks for the great replies and insight. After looking at these ideas, I am leaning toward having the CustomerRepository provide methods for dealing with other items within the customer aggregate, such as addresses, contacts, etc. If you accept that these items are indeed part of a customer aggregate, then the CustomerRepository could provide classes for working with the related items by their ids...

Code:
Address LoadAddress(int addressID)
void DeleteAddress(int addressID)
etc.


One comment contemplated Address as a value object. It probably could be thought of in that way, but I have had a hard time truly getting the hang of value objects in the domain.

Anyway, good discussion. Thx.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Oct 31, 2007 2:00 pm 
Beginner
Beginner

Joined: Wed Aug 29, 2007 1:25 pm
Posts: 26
Location: Recife, PE, Brazil
I always use something almost equals


instead of you do this

Quote:

Code:
Address LoadAddress(int addressID)
void DeleteAddress(int addressID)
etc.




you could do that

Quote:

Code:

Address LoadAddress(Address oAddress)
void DeleteAddress(Address oAddress)




and you would use it like so


Code:

Address address = new Address(1);

address = LoadAddress(address);

Address LoadAddress(Address oAddress)

Address address = new Address(1);

DeleteAddress(address);

void DeleteAddress(Address oAddress)





just because passing an object is always better than pass primitive value either application layers...


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.