Suppose I have
public class Product: Entity
{
public IList<Item> Items { get; set; }
}
Suppose I want to find an item with max something... I can add the method Product.GetMaxItemSmth() and do it with Linq (from i in Items select i.smth).Max()) or with a manual loop or whatever. Now, the problem is that this will load the full collection into memory.
The correct solution will be to do a specific DB query, but domain entities do not have access to repositories, right? So either I do
productRepository.GetMaxItemSmth(product)
(which is ugly, no?), or even if entities have access to repositories, I use IProductRepository from entity
product.GetMaxItemSmth() { return Service.GetRepository<IProductRepository>().GetMaxItemSmth(); }
which is also ugly and is a duplication of code. I can even go fancy and do an extension
public static IList<Item> GetMaxItemSmth(this Product product)
{
return Service.GetRepository<IProductRepository>().GetMaxItemSmth();
}
which is better only because it doesn't really clutter the entity with repository... but still does method duplication.
Now, this is the problem of whether to use product.GetMaxItemSmth() or productRepository.GetMaxItemSmth(product)... again. Did I miss something in DDD? What is the correct way here? Just use productRepository.GetMaxItemSmth(product)? Is this what everyone uses and are happy with?
I just don't feel it is right... if I can't access a product's Items from the product itself, why do I need this collection in Product at all??? And then, can Product do anything useful if it can't use specific queries and access its collections without performance hits?
Of course, I can use a less efficient way and never mind, and when it's slow I'll inject repository calls into entities as an optimization... but even this doesn't sound right, does it?
One thing to mention, maybe it's not quite DDD... but I need IList in Product in order to get my DB schema generated with Fluent NHibernate. Feel free to answer in pure DDD context, though.
UPDATE: a very interesting option is described here: http://devlicio.us/blogs/billy_mccafferty/archive/2007/12/03/custom-collections-with-nhibernate-part-i-the-basics.aspx, not only to deal with DB-related collection queries, but also can help with collection access control.
Having an Items collection and having GetXXX() methods are both correct.
To be pure, your Entities shouldn't have direct access to Repositories. However, they can have an indirect reference via a Query Specification. Check out page 229 of Eric Evans' book. Something like this:
public class Product
{
public IList<Item> Items {get;}
public int GetMaxItemSmth()
{
return new ProductItemQuerySpecifications().GetMaxSomething(this);
}
}
public class ProductItemQuerySpecifications()
{
public int GetMaxSomething(product)
{
var respository = MyContainer.Resolve<IProductRespository>();
return respository.GetMaxSomething(product);
}
}
How you get a reference to the Repository is your choice (DI, Service Locator, etc). Whilst this removes the direct reference between Entity and Respository, it doesn't reduce the LoC.
Generally, I'd only introduce it early if I knew that the number of GetXXX() methods will cause problems in the future. Otherwise, I'd leave it for a future refactoring exercise.
I believe in terms of DDD, whenever you are having problems like this, you should first ask yourself if your entity was designed properly.
If you say that Product has a list of Items. You are saying that Items is a part of the Product aggregate. That means that, if you perform data changes on the Product, you are changing the items too. In this case, your Product and it's items are required to be transactionally consistent. That means that changes to one or another should always cascade over the entire Product aggregate, and the change should be ATOMIC. Meaning that, if you changed the Product's name and the name of one of it's Items and if the database commit of the Item's name works, but fails on the Product's name, the Item's name should be rolled back.
This is the fact that Aggregates should represent consistency boundaries, not compositional convenience.
If it does not make sense in your domain to require changes on Items and changes on the Product to be transactionally consistent, then Product should not hold a reference to the Items.
You are still allowed to model the relationship between Product and items, you just shouldn't have a direct reference. Instead, you want to have an indirect reference, that is, Product will have a list of Item Ids.
The choice between having a direct reference and an indirect reference should be based first on the question of transactional consistency. Once you have answered that, if it seemed that you needed the transactional consistency, you must then further ask if it could lead to scalability and performance issues.
If you have too many items for too many products, this could scale and perform badly. In that case, you should consider eventual consistency. This is when you still only have an indirect reference from Product to items, but with some other mechanism, you guarantee that at some future point in time (hopefully as soon as possible), the Product and the Items will be in a consistent state. The example would be that, as Items balances are changed, the Products total balance increases, while each item is being one by one altered, the Product might not exactly have the right Total Balance, but as soon as all items will have finished changing, the Product will update itself to reflect the new Total Balance and thus return to a consistent state.
That last choice is harder to make, you have to determine if it is acceptable to have eventual consistency in order to avoid the scalability and performance problems, or if the cost is too high and you'd rather have transactional consistency and live with the scalability and performance issues.
Now, once you have indirect references to Items, how do you perform GetMaxItemSmth()?
In this case, I believe the best way is to use the double dispatch pattern. You create an ItemProcessor class:
public class ItemProcessor
{
private readonly IItemRepository _itemRepo;
public ItemProcessor(IItemRepository itemRepo)
{
_itemRepo = itemRepo;
}
public Item GetMaxItemSmth(Product product)
{
// Here you are free to implement the logic as performant as possible, or as slowly
// as you want.
// Slow version
//Item maxItem = _itemRepo.GetById(product.Items[0]);
//for(int i = 1; i < product.Items.Length; i++)
//{
// Item item = _itemRepo.GetById(product.Items[i]);
// if(item > maxItem) maxItem = item;
//}
//Fast version
Item maxItem = _itemRepo.GetMaxItemSmth();
return maxItem;
}
}
And it's corresponding interface:
public interface IItemProcessor
{
Item GetMaxItemSmth(Product product);
}
Which will be responsible for performing the logic you need that involves working with both your Product data and other related entities data. Or this could host any kind of complicated logic that spans multiple entities and don't quite fit in on any one entity per say, because of how it requires data that span multiple entities.
Than, on your Product entity you add:
public class Product
{
private List<string> _items; // indirect reference to the Items Product is associated with
public List<string> Items
{
get
{
return _items;
}
}
public Product(List<string> items)
{
_items = items;
}
public Item GetMaxItemSmth(IItemProcessor itemProcessor)
{
return itemProcessor.GetMaxItemSmth(this);
}
}
NOTE:
If you only need to query the Max items and get a value back, not an Entity, you should bypass this method altogether. Create an IFinder that has a GetMaxItemSmth that returns your specialised read model. It's ok to have a separate model only for querying, and a set of Finder classes that perform specialized queries to retrieve such specialized read model. As you must remember, Aggregates only exist for the purpose of data change. Repositories only work on Aggregates. Therefore, if no data change, no need for either Aggregates or Repositories.
(Disclaimer, I am just starting to get a grasp on DDD. or at least believe doing it :) )
I will second Mark on this one and emphasize 2 point that took me some times to realize.
Think about your object in term of aggregates, which lead to
The point is that either you load the children together with the parent or you load them separately
The difficult part is to think about the aggregate for your problem at hand and not to focus the DB structure supporting it.
An example that emphasizes this point i customer.Orders. Do you really need all the orders of your customer for adding a new order? usually not. what if she has 1 millin of them?
You might need something like OutstandingAmount or AmountBuyedLastMonth in order to fulfill some scenarios like "AcceptNewOrder" or ApplyCustomerCareProgram.
Is the product the real aggregate root for your sceanrio?
What if Product is not an Aggregate Root?
i.e. are you going to manipulate the item or the product?
If it is the product, do you need the ItemWithMaxSomething or do you need MaxSomethingOfItemsInProduct?
Another myth: PI means You don't need to think about the DB
Given that you really need the item with maxSomething in your scenario, then you need to know what it means in terms of database operation in order to choose the right implementation, either through a service or a property.
For example if a product has a huge number of items, a solution might be to have the ID of the Item recorded with the product in the db instead of iterating over the all list.
The difficult part for me in DDD is to define the right aggregates. I feel more and more that if I need to rely on lazy loading then I might have overseen some context boundary.
hope this helps :)
I think that this is a difficult question that has no hard and fast answer.
A key to one answer is to analyze Aggregates and Associations as discussed in Domain-Driven Design. The point is that either you load the children together with the parent or you load them separately.
When you load them together with the parent (Product in your example), the parent controls all access to the children, including retrieval and write operations. A corrolary to this is that there must be no repository for the children - data access is managed by the parent's repository.
So to answer one of your questions: "why do I need this collection in Product at all?" Maybe you don't, but if you do, that would mean that Items would always be loaded when you load a Product. You could implement a Max method that would simply find the Max by looking over all Items in the list. That may not be the most performant implementation, but that would be the way to do it if Product was an Aggregate Root.
What if Product is not an Aggregate Root? Well, the first thing to do is to remove the Items property from Product. You will then need some sort of Service that can retrieve the Items associated with the Product. Such a Service could also have a GetMaxItemSmth method.
Something like this:
public class ProductService
{
private readonly IItemRepository itemRepository;
public ProductService (IItemRepository itemRepository)
{
this.itemRepository = itemRepository;
}
public IEnumerable<Item> GetMaxItemSmth(Product product)
{
var max = this.itemRepository.GetMaxItemSmth(product);
// Do something interesting here
return max;
}
}
That is pretty close to your extension method, but with the notable difference that the repository should be an instance injected into the Service. Static stuff is never good for modeling purposes.
As it stands here, the ProductService is a pretty thin wrapper around the Repository itself, so it may be redundant. Often, however, it turns out to be a good place to add other interesting behavior, as I have tried to hint at with my code comment.
Another way you can solve this problem is to track it all in the aggregate root. If Product and Item are both part of the same aggregate, with Product being the root, then all access to the Items is controlled via Product. So in your AddItem method, compare the new Item to the current max item and replace it if need be. Maintain it where it's needed within Product so you don't have to run the SQL query at all. This is one reason why defining aggregates promotes encapsulation.
Remember that NHibernate is a mapper between the database and your objects. Your issue appears to me that your object model is not a viable relational model, and that's ok, but you need to embrace that.
Why not map another collection to your Product entity that uses the power of your relational model to load in an efficient manner. Am I right in assuming that the logic to select this special collection is not rocket science and could easily be implemented in filtered NHibernate mapped collection?
I know my answer has been vague, but I only understand your question in general terms. My point is that you will have problems if you treat your relational database in an object oriented manner. Tools like NHibernate exist to bridge the gap between them, not to treat them in the same way. Feel free to ask me to clarify any points I didn't make clear.
You can now do that with NHibernate 5 directly without specific code !
It won't load the whole collection into memory.
See https://github.com/nhibernate/nhibernate-core/blob/master/releasenotes.txt
Build 5.0.0
=============================
** Highlights
...
* Entities collections can be queried with .AsQueryable() Linq extension without being fully loaded.
...
Related
I have many things like this in my code (this is just one simple example):
var invoice = context.Invoices
.ForId(invoiceId)
.Include(i => i.Payments)
.Include(i => i.OrderLines)
.First();
And Invoice has field UnpaidAmount caclulated as
public double UnpaidAmount
{
get
{
return OrderLines.Sum(ol => ol.Amount) -
Payments.Sum(p => p.Amount);
}
}
Now, what happens often in the project is that if someone needs to modify UnpaidAmount logic to this for instance:
return OrderLines.Sum(ol => ol.Amount) -
Payments.Sum(p => p.Amount) -
CreditNotes.Sum(cn => cn.Amount);
Then they would need to find everywhere in project where UnpaidAmount is used and add CreditNotes to Include when fetching Invoice. People often forget that and, in this case, Sum on CreditNotes actually gets called on an empty collection, instead on one fetched from database.
This becomes really buggy and hard to maintain through project.
The alternative is to either lose LazyLoading so we dont have to think about includes anywhere but this can lead to performance problems which might not be detected during the develpoment but later in production when number of records fetched gets larger.
Or to have one method which fetches Invoice object with all of its navigation properties + recursively doing it for navigation properties deeper in the object graph. But that would be overkill because many things which are not needed would be fetched every time.
I assume it is trade off which I will have to make, but I just need advice from people who faced with this kind of problem on larger projects, what solution do you think is most maintainable for long run?
Then they would need to find everywhere in project where UnpaidAmount is used and add CreditNotes to Include when fetching Invoice.
Why does this responsibility live in more than one place?
The alternative is to either lose LazyLoading so we dont have to think about includes anywhere but this can lead to performance problems which might not be detected during the develpoment but later in production when number of records fetched gets larger.
Or to have one method which fetches Invoice object with all of its navigation properties + recursively doing it for navigation properties deeper in the object graph. But that would be overkill because many things which are not needed would be fetched every time.
What I think you are looking for is the Repository Pattern.
The basic idea:
You have one or more role interfaces that consumers can use to describe how they are going to use the Invoice (or alternatively, which view of the invoice satisfies their needs).
You provide implementations of those roles that share a common understanding of how the invoice data is stored. This means that when you introduce CreditNotes into the model, there's only one place that needs to change.
You use your plumbing (dependency injection, or whatever) to ensure that the correct implementation is provided for each role.
In short, you create an explicit contract between the consumers and the suppliers; the consumers describe what they need, the suppliers have freedom of choice in how they meet that need.
Udi Dahan wrote a few posts related to this idea, back in the day.
Better Domain Driven Design Implementation
Query Objects vs Methods on a Repository
Fetching Strategy Design
Intentions and Interfaces
Can offer to introduce some kind of Store, called, for example, Invoices. It will get DbContext in constructor, and provide all the methods you have to fetch and save invoices. So, you will have only one place to write and modify query.
class Invoices
{
public Invoices(DbContext dbContext){....}
public Invoice GetInvoiceById(int invoiceId)
{
return this.dbContext.Invoices
.ForId(invoiceId)
.Include(i => i.Payments)
.Include(i => i.OrderLines)
.FirstOrDefault();
}
...
I have been struggling to understand DDD. Here is a scenario that boggles me. Say we have the entity Fund which has value object allocation/holdings and historical prices. What if a service only wants allocations of a particular fund? Should we return a list of allocation objects or return a Fund entity that contains a list of allocations? If we resort to the first approach, we need to create an Allocation Repository. The second approach seems a bit weird, since the entity is being modified to return only certain value objects to the service. Without much knowledge about the entity, shouldn't the service have all fund fields accessible to it?
My description might not be accurate. Please let me know if I need to clarify my post.
class Fund
{
int fundId;
List<Allocation> allocations;
List<Holding> holdings;
}
class Allocation
{
string type;
string percentage;
}
To answer the question in the title, no you should not. The repository pattern only works if the items in the repository have identity. If an object has identity then it is an entity not a value object.
Value objects should be all or nothing, e.g. changing one property on a value object replaces the entire thing. Thus a value object is immutable after creation.
That is not to say that a version of a value object internal to the repository cannot have an identity, but you should not let persistence concerns alter your domain.
Based on your description it actually sounds like Allocation is an entity, because it is differentiable and thus has identity.
Assuming that Allocation is an entity, the question I would then be asking is should Allocation be its own aggregate.
There is multiple variations of repository implementations but I would not mind returning a list of Allocation IF, and ONLY IF, Allocation is never managed on it's own.
In other words, if you will, at some point, want to get information about an Allocation, no matter which Fund it belongs to, then you will need a repository for Allocations, and if you are making such a repository, then you should have a method like getAllocationsbyFundId(int id) or somethign similar. If it doesn't make sense to look at Allocations on their own without knowning which Fund it is from, then Allocations are really a part of Fund and it would make complete sense to have a method on your Fund repository to return the Allocations of a specific Fund.
If you, however, end up with a GetAllAllocation() method on your Fund repository, then you have slipped out of a clean pattern.
I may not quite understand your domain so let me know if I get this wrong. When we take the Order / OrderLine scenario we may model OrderLine as a VO (much like your Fund / Allocation). Why would we ever want to query a service to return just a list of the OrderLine objects for an Order? :)
However, if you really need to do this you should be loading the Fund instance and using its contained Allocations list. However, querying your domain model usually leads to problems (lazy-loading, fetching strategies, and moving away from tell-don't-ask). If you do need to query, consider creating a lightweight query model (some call it a read model) that performs this function.
So I concur with Mgetz that you should have a repository of VOs. If you have a fixed list of VOs then you could use a type of enum structure. In C# you could do this with readonly class instances. Vaughn Vernon calls this 'Standard Types' (if memory serves). I don't think you have that scenario, though.
I have an ASP.NET MVC application coded with C#. The application is structured this way:
Controller
Repository
LINQ to Entities (Entity Framework)
View
I use the Repository (_ProductRep) to query the LINQ to Entities and give to the Controller actual entities or List<T>, not IQueriables<T>.
I would like to have some help about a situation where I have more than a doubt. I have the following code:
List<Monthly_Report> lproduct_monthlyReport = _ProductRep.GetArchiveReport(product.Prod_ID, lmonth, lyear);
After I get this lproduct_monthlyReport I need to query it inside a foreach and get a specific record. Currently I implemented the solution like this:
foreach (var item in litemList)
{
var lproductItem_monthlyReport = lproduct_monthlyReport.Single(m => m.Item_ID == item.Item_ID);
// Other code
}
Where litemList is the list of all the possible items a product can have.
I wanted to know whether this solution sensibly increase the coupling (and violates the law of Demeter) or it is acceptable because I am actually querying a List<T> and not an IQueriable<T>. Correct me if I am wrong, but I guess that since the List does not need to access the EF DataContext, there is no coupling between Controller and EF.
In case I am wrong, the only solution I can think about is to substitute the query with a Repository method (that still I have to implement):
var lproductItem_monthlyReport_ProductRep.GetArchiveReport(product.Prod_ID, lmonth, lyear, item.Item_ID);
with this solution however the Repository makes one query with 4 conditions every loop cycle whilst in the previous solution the repository was making a query with just one conditions.
May you please enlighten me on this issue? Thanks.
PS: I need both variables lproduct_monthlyReport and lproductItem_monthlyReport inside the loop, I cannot just use one of them
PPS: I know that I should have a Business Service Layer between Controller and Repository, it is my next step.
Returning Lists from your repository will give you awful performance, because you lose the deferred execution behaviour. Basically your repository will retrieve every single record, and not related entities, into memory, and turn them into a List, which then gets processed in memory. If you want to access a related entity, it'll need another database hit. If you stick with IEnumerable (or IQueryable), then you are hiding the nuances of the entity framework behaviour from the client, but still getting the advantages like lazy loading and deferred execution.
Ignoring the specifics of your Repository for now, if you do this:
List<Product> products = MyEntities.Products.ToList();
Product product1 = products.Single(p => p.Id = 1);
it will perform much worse than this:
IEnumerable<Product> products = MyEntities.Products;
Product product1 = products.Single(p => p.Id = 1);
The first one will perform a SELECT in the database with no WHERE clauses, then instantiate .Net objects for every result, then query that in-memory list. The second will do nothing until you access a property on product1 and will at that point issue a database command to just retrieve the 1 product, and only instantiate that 1 product.
The difference between the 2 may not be noticeable with small data sets, but as the data set gets larger this will get worse and worse. Throw in a connected entity (or worse still entity collection), and you'll get potentially thousands of database hits, where if you stuck with IEnumerable you'd get 1.
I would probably have function like this GetArchiveReport(int prodID, int lmonth, int lyear, IEnumerable<int> itemIDs) that would do a itemIDs.Contains(tbl.ID) inside your query
var SelectedReports = _ProductRep.GetArchiveReport(product.Prod_ID, lmonth, lyear, litemList.Select(item => item.Item_ID));
foreach(var prodItem in SelectedReports)
{
//Do code
}
I'm having difficulty wrapping my head around business objects or more specifically, business object collections.
Here's a quick example of what I'm trying to do.
If I have an Incident Object, this object can have a number of people involved and each of those Person objects can have multiple notes. Notes can't exist without a Person object and Person objects can't exist without an Incident Object.
If I have Public List<Note> notes = new List<Note>() then methods such as ADD and REMOVE become available to Person within Incident. I assume that if I was to call those methods on the Notes collection it will simply remove it from the List but not execute any code to actually add/update/delete the employee from the data source. This leads me to believe that I shouldn't use List but something else?
This also leads me to another question. Where should the actual database CRUD operations reside. Should a Note object have its own CRUD or should the Person object be responsible for it since it can't exist without it?
I'm a little lost about which way to go and I'd like to get this part right because it will be the template for the rest of the program.
Some great information has been given but one thing that you mentioned that may be confusing you is this:
"If i have Public List notes = new
List() then methods such as ADD,
REMOVE become available to Person
within Incident."
That all depends on how you design your classes. One thing that you should think about is the way this data relates to one another. That will help you picture your class design.
It sounds like the following:
One incident can involve many people
One person can create many notes
A note is the lowest level and exists due to an incident being created and a responsible person(s) working on that incident.
Incident 1 - many Persons
Person 1 - many notes
You can do this type of relationship in a number of ways. One way may be to actually seperate the objects involved, and then create joined objects.
For instance
public class Incident {
//insert incident fields here
//do not add person logic / notes logic
//probably contains only properties
}
public class Person {
//insert person fields
//private members with public properties
//do not embed any other logic
}
public class Comment {
//insert comment private fields
//add public properties
//follow the law of demeter
}
These classes do not give details to one another, they are just repositories to store this information. You then relate these classes to one another for instance
public class IncidentPersonnel {
List<Person> p;
//add methods to add a person to an incident
//add methods to remove a person from an incident
....
}
Then you may have another class handling the commenting by personnel
public class PersonnelNotes {
List<Note> n;
//other methods...
}
You can go further with this but it may complicate things but I am just giving you another idea of how to handle this.
Try to follow the law of demeter for functions
Encapsulate all of your objects, in addition, your neighbor can talk to you but not much else... This will help keep your classes loosely coupled and makes the thought process a bit simpler for you.
Finally, you mentiond how the CRUD operations should work. This all goes back to your DAL (Data Access Layer). Rather then return rows of data from a table you could then return a referenced object with all of its attributes. Add's and remove's work the same way (passing in or out an object). You can use an ORM or write up your own DAL. It all depends on how involved you want to involve yourself :).
You have several different questions in one here, I will try to answer most.
In regards to problems using List<T> - the framework has a ReadOnlyCollection<T> that is useful in exactly your situation. This is a collection that does not allow adding or removing once created.
In regards to CRUD operation responsibility - that should belong to your data layer, not any of your objects (see SRP - Single Responsibility Principle).
The way I do it is: each object that has children objects contains a list of them, and each object with a parent contains a property with its type. Adding is done by populating an object (or an hierarchy of objects) and sending to the DAL for persistence if desired. The CRUD operations are all in the DAL, which is agnostic of the object types but uses such types to determine which tables, columns, etc to access. Deleting is the only thing dealt with differently by setting an object's Deleted property which triggers the DAL to remove it.
Now regarding business logic - it does not reside with the objects themselves (the DAOs) but rather it is done by classes that receive or gather such DAOs when necessary, perform their work and send the DAOs back to the DAL for updates.
I had a quick question about the proper object relationship I should set up for this situation:
I have a Customer object with associated parameters and a depot object with associated parameters. Each depot serves a set of customers and the customer needs access to particular information for their respective depot.
I'm wondering what the proper relationship I should set up so that a set of customer objects all reference the same instance of a particular depot object. I wanted to be sure it wasn't creating a duplicate Depot object for each customer. Furthermore, i'd like to be able to change properties of the Depot without going through the customer itself.
I know this is probably a fairly basic question but C# has so many different "features" it gets confusing from time to time.
Thanks for your help!
Charlie
If I understand your question correctly, I think a solution to your problem might be an OR mapper. Microsoft provides two OR mappers at the moment, LINQ to SQL and Entity Framework. If you are using .NET 3.5, I recommend using LINQ to SQL, but if you are able to experiment with .NET 4.0, I would highly recommend looking into Entity Framework. (I discourage the use of Entity Framework in .NET 3.5, as it was released very prematurely and has a LOT of problems.)
Both of these OR mappers provide visual modeling tools that allow you to build a conceptual entity model. With LINQ to SQL, you can generate a model from your database, which will provide you with entity classes, as well as associations between those classes (representing your foreign keys from your DB schema). The LINQ to SQL framework will handle generating SQL queries for you, and will automatically map database query results into object graphs. Relationships such as the one you described, with multiple customers in a set referencing the same single department are handled automatically for you, you don't need to worry about them at all. You also have the ability to query your database using LINQ, and can avoid having to write a significant amount of stored procedures and plumbing/mapping code.
If you use .NET 4.0, Entity Framework is literally LINQ to SQL on steroids. It supports everything LINQ to SQL does, and a hell of a lot more. It supports model-driven design, allowing you to build a conceptual model from which code AND database schema are generated. It supports a much wider variety of mappings, providing a much more flexible platform. It also provides Entity SQL (eSQL), which is a text-based query language that can be used to query the model in addition to LINQ to Entities. Line LINQ to SQL, it will solve the scenario you used as an example, as well as many others.
OR mappers can be a huge time, money, and effort saver, greatly reducing the amount of effort required to interact with a relational database. They provide both dynamic querying as well as dynamic, optimistic updates/inserts/deletes with conflict resolution.
This sounds like you've got a Many-to-many relationship going on. (Customers know about their Depots, and vice versa)
Ideally this seems best suited for a database application where you define a weak-entity table ... Of course using a database is overkill if we're talking about 10 Customers and 10 Depots...
Assuming a database is overkill, this can be modeled in code with some Dictionarys. Assuming you're using int for the unique identifiers for both Depot and Customer you could create something like the following:
// creating a derived class for readability.
public class DepotIDToListOfCustomerIDs : Dictionary<int,List<int>> {}
public class CustomerIDToListOfDepotIDs : Dictionary<int,List<int>> {}
public class DepotIDToDepotObject : Dictionary<int,Depot>{}
public class CustomerIDToCustomerObject : Dictionary<int, Customer>{}
//...
// class scope for a class that manages all these objects...
DepotIDToListOfCustomerIDs _d2cl = new DepotIDToListOfCustomerIDs();
CustomerIDToListOfDepotIDs _c2dl = new CustomerIDToListOfDepotIDs();
DepotIDToDepotObject _d2do = new DepotIDToDepotObject();
CustomerIDToCustomerObject _c2co = new CustomerIDToCustomerObject();
//...
// Populate all the lists with the cross referenced info.
//...
// in a method that needs to build a list of depots for a given customer
// param: Customer c
if (_c2dl.ContainsKey(c.ID))
{
List<int> dids=_c2dl[c.ID];
List<Depot> ds=new List<Depot>();
foreach(int did in dids)
{
if (_d2do.ContainsKey(did))
ds.Add(_d2do[did]);
}
}
// building the list of customers for a Depot would be similar to the above code.
EDIT 1: note that with the code above, I've crafted it to avoid circular references. Having a customer reference a depot that also references that same customer will prevent these from being quickly garbage collected. If these objects will persist for the entirety of the applications lifespan a simpler approach certainly could be taken. In that approach you'd have two lists, one of Customer instances, the other would be a list of Depot instances. The Customer and Depot would contain lists of Depots and Customers respectively. However, you will still need two dictionaries in order to resolve the Depot IDs for the customers, and vice versa. The resulting code would be 99% the same as the above.
EDIT 2:
As is outlined in others replies you can (and should) have an object broker model that makes the relationships and answers questions about the relationships. For those who have misread my code; it is by no means intended to craft the absolute and full object model for this situation. However, it is intended to illustrate how the object broker would manage these relationships in a manner that prevents circular references. You have my apologies for the confusion it caused on the first go around. And my thanks for illustrating a good OO presentation that would be readily consumed by others.
In reply to #Jason D, and for the sake of #Nitax: I'm really skimming the surface, because while it's basically easy, it also can get complicated. There's no way I'm going to re-write it better than Martin Fowler either (certainly not in 10 minutes).
You first have to sort out the issue of only 1 object in memory that refers to a specific depot. We'll achieve that with something called a Repository. CustomerRepository has a GetCustomer() method, and the DepotRepository has a GetDepot() method. I'm going to wave my hands and pretend that just happens.
Second you need to need to write some tests that indicate how you want the code to work. I can't know that, but bear with me anyways.
// sample code for how we access customers and depots
Customer customer = Repositories.CustomerRepository.GetCustomer("Bob");
Depot depot = Repositories.DepotRepository.GetDepot("Texas SW 17");
Now the hard part here is: How do you want to model the relationship? In OO systems you don't really have to do anything. In C# I could just do the following.
Customers keep a list of the depots they are with
class Customer
{
public IList<Depot> Depots { get { return _depotList; } }
}
alternatively, Depots keep a list of the customers they are with
class Depot
{
public IList<Customer> Customers { get { return _customerList; } }
}
// * code is very brief to illustrate.
In it's most basic form, any number of Customers can refer to any number of Depots. m:n solved. References are cheap in OO.
Mind you, the problem we hit is that while the Customer can keep a list of references to all the depot's it cares about (first example), there's not an easy way for the Depot to enumerate all the Customers.
To get a list of all Customers for a Depot (first example) we have to write code that iterates over all customers and checks the customer.Depots property:
List<Customer> CustomersForDepot(Depot depot)
{
List<Customer> allCustomers = Repositories.CustomerRepository.AllCustomers();
List<Customer> customersForDepot = new List<Customer>();
foreach( Customer customer in allCustomers )
{
if( customer.Depots.Contains(depot) )
{
customersForDepot.Add(customer);
}
}
return customersForDepot;
}
If we were using Linq, we could write it as
var depotQuery = from o in allCustomers
where o.Depots.Contains(depot)
select o;
return query.ToList();
Have 10,000,000 Customers stored in a database? Ouch! You really don't want to have to load all 10,000,000 customers each time a Depot needs to determine its' customers. On the other hand, if you only have 10 Depots, a query loading all Depots once and a while isn't a big deal. You should always think about your data and your data access strategy.
We could have the list in both Customer and Depot. When we do that we have to be careful about the implementation. When adding or removing an association, we need to make the change to both lists at once. Otherwise we have customers thinking they are associated with a depot, but the depot doesn't know anything about the customer.
If we don't like that, and decide we don't really need to couple the objects so tightly. We can remove the explicit List's and introduce a third object that is just the relationship (and also include another repository).
class CustomerDepotAssociation
{
public Customer { get; }
public Depot { get; }
}
class CustomerDepotAssociationRepository
{
IList<Customer> GetCustomersFor(Depot depot) ...
IList<Depot> GetDepotsFor(Customer customer) ...
void Associate(Depot depot, Customer customer) ...
void DeAssociate(Depot depot, Customer customer) ...
}
It's yet another alternative. The repository for the association doesn't need to expose how it associates Customers to Depots (and by the way, from what I can tell, this is what #Jason D's code is attempting to do)
I might prefer the separate object in this instance because what we're saying is the association of Customer and Depot is an entity unto itself.
So go ahead and read some Domain Driven Design books, and also buy Martin Fowlers PoEAA (Patterns of Enterprise Application Architecture)
Hope this is self-explanatory.
OO:
ER: