1

I am trying to create modular (component based) application.

My scenario is so simple, I have a SalesManagement component which contains Invoice, and I have PartyManagement component which contains Customer,

so far so good, Then I defined a class for Customer as

public class Customer {
   public int ID {get;set;}
   public string Name {get;set;}
}

and Invoice as

public class Invoice {
   public int ID {get;set;}
   public int CustomerID {get;set;}
   public Customer Customer {get;set;}
}

in order to have Customer reference in Invoice I referenced PartyManagement in SalesManagement component

then I tried to add a collection of Invoice under customer as

public class Customer {
   public int ID {get;set;}
   public string Name {get;set;}
   public virtual ICollection<Invoice> Invoices {get;set;}
}

to make it happen I have to reference InvoiceManagement in PartyManagement which makes a circular dependency between assemblies in compile time

Any idea how to resolve this design issue?

p.s If I keep everything in 1 assembly there is no problem, I want to keep them in separate assemblies

3
  • Simple : Put Invoices into Invoice instead of Customer. Commented Jun 4, 2017 at 22:16
  • How does it make sense to have a collection of Invoices inside Invoice?? I need to have a navigation property from customer, to retrieve his invoices Commented Jun 4, 2017 at 22:27
  • If it is static it makes perfect sense. Do it all the time. Commented Jun 5, 2017 at 2:46

2 Answers 2

1

"p.s If I keep everything in 1 assembly there is no problem, I want to keep them in separate assemblies"

Don't. Unless you absolutely have to, this is just going to cause you repeated problems like what you're already seeing.

But, if there is a valid reason to keep your entities in separate assemblies then you either: A) define shared entities in a common assembly shared across other business-specific assemblies, (chances are this will bubble out to a point where you might as well have just 1 assembly.) B) use bounded contexts so that a "Customer" and/or "invoice" as it relates to one set of business entities can remain independent and unique compared to references consumed by other business entities.

If the parent entities cannot exist in the same assembly with each other, then you should strongly reconsider attempting to "share" any of their children. Given Assembly A deals heavily with Customers, and Assembly B deals heavily with Invoices, but would cause a circular reference back to (A) to associate a Customer with an Invoice, then what you should look at having is a concept of a Customer and Invoice (if needed) within Assembly A, and a Customer and Invoice defined in Assembly B. Chances are if Logic using Assembly A entities "uses" any data from Invoices it would be minimal, or not even used at all. The entity for an "Invoice" could be kept very lightweight. Assembly B might deal more heavily with Invoices, and only need token information about Customers.

By splitting the domain you can keep the implementation of entities fit for purpose which can improve performance. So for instance when you're dealing with something from Assembly A where you want Customer details and don't care about invoices, the Customer implementation in A doesn't even have Invoices mapped, or a simple invoice entity serving as a summary view. (I refer to these entities in my bounded contexts as "lightweights") Just keep in mind that only one context should serve as the responsible party for any given entity. (Only one bounded context mapping a "heavyweight" entity) In the above example, the heavyweight Customer would reside in assembly A, with the heavyweight Invoice in Assembly B. Lightweights can be shared between bounded contexts by using file links in the projects but that's often more trouble than it's worth.

Sign up to request clarification or add additional context in comments.

Comments

0

If you specifically require linking both ways, then you can use your ORM to handle this by lazy loading any linked entities - which you're already doing with your Customer.Invoices collection, since it has the virtual keyword applied.

If you haven't come across this before, it means that the linked entity or collection will not be loaded until it is referenced, which will prevent the circular reference causing a problem with your data access, but still allows you to use the pattern.

I'm assuming you're using Entity Framework, in which case you can read more about this here: https://msdn.microsoft.com/en-us/library/jj574232(v=vs.113).aspx

Edit - what I would do, to avoid circular references*

|Solution

|-- ProjectOne (references Dtos and Repositories)

|-- ProjectTwo (references Dtos and Repositories)

|-- Repositories (references Entities & Dtos, but only returns Dtos)

|-- Entities (doesnt reference any other projects - EF context is here)

|-- Dtos (doesnt reference any other projects)

This means that both ProjectOne & ProjectTwo are decoupled from our DataAccess implementation, and we can easily model our Dto model relationships since they are all in the same Dtos project and also not restricted by our database "Entities" model - meaning we could combine data from different sources or make it more easily consumable by client code.

*There would also be interfaces covering our Repository layer here (and probably a Service layer as well in a larger solution). I've left this out to simplify the layout.

7 Comments

Right now circular dependency happens in compile time, since each assembly is referencing the other one
If you mean that you have your Entities in different projects, then put all your models into another project which just holds your models, and reference that project in any other project that requires it.
Yes, as I mentioned in my question If I put all models in 1 assembly it's fine, but I want to have them in their own assemblies
I actually assumed that was a typo - as its quite an unusual requirement - for reasons like this. As food for thought, why would you want to separate Invoices and Customers when they are linked entities?
It's just a sample, my concern is two entities that have relation but in different assemblies, usually entities have relation to entities in other domains
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.