EF Core without additional repository pattern - c#

There seems to be a certain movement advocating that when we use EF Core we should avoid creating a Repository & Unit of work pattern because EF Core already implement those two and we can leverage this implicit implementation. That would be great because implementing those patterns is not always as straightforward as it would seem.
So here's the problem. When implementing repository the 'classic' way we have a place to put the code that builds our domain objects. Let me explain with an example; we have an Invoice and an InvoiceRow entities. Each Invoice has many InvoiceRows. I included only the navigational property for brevity.
public class Invoice
{
public int Id { get; set; }
public DateTime Date { get; set; }
public decimal Total { get; set; }
public List<InvoiceRow> InvoiceRows { get; }
}
public class InvoiceRow
{
public int Id { get; set; }
public Invoice Invoice { get; set; }
public decimal UnitPrice { get; set; }
public decimal RowPrice { get; set; }
}
Now, my business object is an Invoice with its rows, and this should be the only way to manipulate the invoices.
When using 'explicit' repository we would do something like:
public class InvoicesRepo
{
public AppDbContext AppDbContext { get; private set; }
public Invoice Find(int id)
{
return
AppDbContext.Invoices.Where(invoice => invoice.Id == id)
.Include(nameof(InvoiceRow))
.First();
}
}
This restricts the access to the Invoice to the method [InvoicesRepo].Find(id) that builds the invoice in the way that is expected by the domain logic code.
Is it possible to achieve this with bare EF Core? Maybe working with visibility of DbSets and/or additional features that I don't know? Since this seems to be quite a fundamental functionality of a full-blown repository, if it's not achievable, have I just destroyed the main argument of experts advocating for no (additional) repository when using EF Core?

Is it possible to achieve this with bare EF Core? Maybe working with visibility of DbSets and/or additional features that I don't know?
Sure, accepting that the DbContext is your repository doesn't mean you can't make design decisions and you have to have use the default DbContext design.
You can add reusable data access code to your DbContext for convenience and consistency, eg methods like:
public Invoice FindInvoice(int id)
{
this.Invoices.Where(invoice => invoice.Id == id)
.Include(nameof(InvoiceRow))
.First();
}
So for code that needs the standard shape of Invoice with InvoiceRows, they call this method. But for code that needs some nonstandard shape, they still can access the DbSets or IQueryable methods and construct a custom query.
You can even eliminate the DbSet properties, to more strongly guide users to use your custom methods, like:
public IQueryable<Invoice> Invoices => this.Invoices.Include(nameof(InvoiceRow));
Then to get Invoices without InvoiceRows a consumer would either add a custom projection to this, something like
db.Invoices.Where(i => i.CustomerID == custId).Select(i => new InvoiceDTO(i)).ToList();
or access the DbSet
var invoice = db.Set<Invoice>().Find(invoiceId);
And you can organize the methods on your DbContext by having it implement various interfaces.

Ok, this is just a preliminary answer based on many readings all around.
public class InvoiceQuery
{
public AppDbContext AppDbContext { get; private set; }
public int Id { get; set; }
public Invoice Execute()
{
return AppDbContext
.Invoices
.Include(nameof(Invoice.InvoiceRows))
.Where(invoice => invoice.Id == Id)
.FirstOrDefault();
}
}
My problem was that this is not substantially different from what we would have into a repository. That's from a practical point of view; from a theoretical point of view, this puts you on the right perspective while the repository is sort of misleading.
The reason why it's misleading is that there is no 1-1 association between entities and actions or queries that you can do on the database. Even in this case, Invoice is not just invoice, but is Invoice plus InvoiceRows. (By the way, I think that InvoiceQuery is a good name (and not InvoiceWithRowsQuery) because from a business logic point of view an invoice is a full-loaded invoice; an invoice with 0 rows is an empty invoice, not an partially-loaded invoice.)
So a query focuses on what you get, not on the entity you start from, because they can be more than one.
This "query" name is sort of counter-intuitive, because one would say that as you move towards the business logic, you stop seeing things like queries and you start seeing things like paramenters. And actually we have parameters, "query" is only the name of the container. Maybe we should call it Business Object Query, but it would be too long, but that's the meaning. So this query object is just a simple container for an EF Query; I will talk about this later on. In this class based implementation, besides being glorified as object, there is no addictional functionality. Maybe you would expect some additional functionality, instead you have something less. This missing thing is that they are no associated with an entity anymore. This is another counter-intuitive fact that proves that here we are dismantleing something: the contrived entity-repository view.
Having a so simple object poses a question. Why not a method on some related class? If we put it on the Entity, we go back to the repository-like organization, that seems flawed mainly because the missing 1-1 bla bla means that in many cases you couldn't tell which entity associate a query to. But some authors use a sort of 'container class' that is really no more than a container just it's not entitled to an entity (OrdersData for example). If you like me like classes that have a clear purpose, this sends you shivers down the spine. We started up saying that we have a solution that is better than the repository. That the repository has so many problems. We end up with a class that has "Data" in its name. How could a better solution be so lame? It feels like the repository alternative is just a bunch of stuff, not a steady and well designed as expected. On the other hand, this is just a way to collect queries (the same queries that you would have in a repository, the very same query that you would have in a query/command pattern...) under a name that is not that of an entity.
Yes, all this seems to boil down to a naming choice. Advocates of repository pattern and advocates of no-repository fiercely fighting each others. Then you look at the code, what it actually does. The code is the same and the names are all we are talking about. That is: if you look what repositoriests do in their repositories and non-repositoriests do in their whatever (not always clear) classes, they do exaclty the same things. But this is just an impression, I've to time-proof it.
In the repository model, having a repository for entity with its methods was reassuring. It provided a sort of scaffolding where to put all of the methods. Unfortunately that scaffolding seems to be too limitating, again because of the not 1-1 relation between entities and queries/commands. Yet we have to say that the root entities like Invoice (in DDD speaking Invoice are root, InvoiceRows are aggregate) would fit quite well in repository-style organization.
For this reason, in this query based solution the flatness of the Query collection is not fully satisfying, and this is another reason why I think I want to further elaborate on this topic.

Related

It is possible to use child class to implement Separation of concerns using EF Core?

My goal is async loading of related entities using DBContext.
Let imagine two projects. The first named MyApp.Domain and contains domain entities.
namespace MyApp.Domain
{
public class PlanPage
{
public Guid Id { get; set; }
}
}
namespace MyApp.Domain
{
public class PlanPageDay
{
public Guid Id { get; set; }
public Guid PlanPageId { get; set; }
}
}
The second project named MyApp.Infrastructure.EntityFramework and contains configuration of projection entities to database. It also contains class which extends domain entity and implements Entity framework specific logic.
namespace MyApp.Infrastructure.EntityFramework.Models
{
public class PlanPageEntity : PlanPage
{
private readonly ApplicationDbContext _applicationDbContext;
protected PlanPageEntity(ApplicationDbContext applicationDbContext)
{
_applicationDbContext = applicationDbContext;
}
public ICollection<PlanPageDay>? Days { get; set; }
public async Task<ICollection<PlanPageDay>> GetDays()
{
return Days ??= await _applicationDbContext.PlanPageDays
.Where(pd => pd.PlanPageId == Id)
.ToListAsync();
}
}
}
The purpose of this example is simple. We separate infrastructure code from domain code. Look how do we plan to use this concept:
// Entity initializing code. Placing somewhere in domain logic.
var plan = new PlanPage(/*some constructor arguments*/);
// Entity loading code. Placing somewhere in infrastructure implementation.
public async Task<PlanPage> GetPlanPage(Guid id)
{
return await _applicationDbContext.Set<PlanPageEntity>().FindAsync(id);
}
Note that we tell to Entity framework to use child class (PlanPageEntity) so it can handle all specific things that it can.
The question is: Is it possible to configure the EF so that it allows us to use this concept?
As requested here's a little more details for my opinion stated in the comments.
The main reason why I think your current approach is a bad idea is that it violates the separation of concerns design principle: when you are mixing domain models with data access models, you make your domain logic completely dependent on how you model the data in your database. This quickly limits your options because the database may have some restrictions on how you can model your data that doesn't fit well with the domain logic you want to implement as well as making maintenance difficult. E.g. if you decide to split up one DB table into two then you might have a big task ahead of you in order to make your domain logic work with those two new models/tables. Additionally, making performance optimizations in your database easily becomes a nightmare if not thought through ahead of time - and you shouldn't spend time thinking of optimizing your system before it's necessary.
I know this is a little abstract since I don't know much about your domain but I'm sure I could find more arguments against it.
Instead, separating data access models (and in general all external data models) from your domain models makes it much easier to maintain: if you need to make some changes to your database, you simply need to update the logic that maps the data from your data access models to your domain model - nothing in your domain logic needs to change.
In the examples you have given, you have already logically separated your domain models and data access models into two separate projects. So why not follow through with that thought and separate the two with a binding/mapping layer in-between?
Is it possible to configure the EF so that it allows us to use this concept?
Yes. Essentially you have DTO's, and your Entities derive from your DTOs. So when you fetch an Entity you can return it directly. But if you wouldn't be able to attach a non-Entity, so you'd have to map it. It's going to be inconvenient, and like 99.999% of bespoke entity and repository designs, will be ultimately a waste of time.
This is somewhat similar to the what EF already does for you. Start with persistence-ignorant Entity classes, and introduce persistence-aware runtime subtypes for scenarios that require them, which is basically just Lazy Loading.

How do I use the UserManager from project A in project B?

Both project A and project B are ASP NET Core 2.2 apps.
Project B uses Hangfire for background jobs and does very little else, and the fact that it uses Hangfire may not even be important (more on this at the bottom). Project A enqueues jobs on B's Hangfire.
Now, let's say I have my class representing a task, called Job. This is contained in project C, a plain old class library referenced by project B, and which in turns references other projects containing the entities it's working with.
Dependencies are to be injected into this class through the constructor:
public class Job
{
public Job(UserManager<ApplicationUser> userManager,
IThisRepository thisRepository,
IThatRepository thatRepository)
{
}
public void Execute(string userId)
{
// this is where the work gets done
}
}
and for the most part they do get injected: IThisRepository and IThatRepository are injected and they work... mostly.
In project B's Startup.cs, the one that is supposed to run this job, I manually and successfully registered those interfaces, along with the DbContext that they require a some other stuff.
UserManager was quite a bit harder to register manually because of all the parameters its constructor requires, so since I didn't really need it inside my job, I just decided to make a few changes.
Now, an example of the entities I'm working with is as follows:
public class Category
{
[Key]
public int Id { get; set; }
// several other properties of primitive types
public ApplicationUser User { get; set; }
[Required]
public string UserId { get; set; }
}
public class Dish
{
[Key]
public int Id { get; set; }
// several other properties of primitive types
public ApplicationUser User { get; set; }
[Required]
public string UserId { get; set; }
public Category Category { get; set; }
[Required]
public string CategoryId { get; set; }
}
now the problem is this: inside of Job I try to create a new Dish and associate it with both the user and the category. Since I just have the user id and I don't have access to UserManager, this is what I try to do:
// ...
var category = await categoryRepository.FindByUserAndCode(userId, "ABC");
// this is a category that is guaranteed to exist
var dish = new Dish();
dish.UserId = userId;
// notice there's no dish.User assignment, because I don't have an ApplicationUser object
dish.Category = category;
dishRepository.Upsert(dish); (which internally either creates a new entity or updates the existing one as appropriate)
and this is where it all breaks down, because it says that a category with the same Id I'm trying to insert is already present, so I'm trying to duplicate a primary key.
Since the category with code ABC for this user exists in the db, I thought it was odd.
Here's the thing: the instance of Category that the repository returns does have it's UserId property populated, but the User property is null.
I think this is what causes my problem: EF probably sees that the property is null and considers this object a new one.
I don't know why it comes up null (and it does even for other entities that all have a property referencing the user), but I tried to backtrack and, instead of using just the user id, I wanted to try to get Hangfire to instantiate Job injecting UserManager<ApplicationUser> into it, so at least I could get an instance of my user by its id.
It's worth noting that this works in other parts of project A, it's just that when I'm executing the background job something goes horribly wrong and I can't for the life of me figure out what it is.
However the dependencies of UserManager are many, and I fear I might be barking up the wrong tree or doing it completely wrong.
I said that the fact I'm using Hangfire might not matter because the assumption under which it operates is: just give me the name of your class, I'll take care of instantiating it as long as all the dependencies have been registered.
Anyone has done this before and can help shed some light?
You've included an absolute ton of information here that is entirely inconsequential to the problem at hand. What your issue boils down is simply the exception you're getting when attempting to add a dish: "a category with the same Id I'm trying to insert is already present, so I'm trying to duplicate a primary key."
This is most normally caused by attempting to use a detached entity as a relationship, i.e.:
dish.Category = category;
If category is detached from the context, then EF will attempt to create it because of this assignment, and since it already exists, that creation fails. We can't see what's going on in categoryRepository.FindByUserAndCode, but I'd imagine you're either calling AsNoTracking with the query, or are newing up an instance of Category manually yourself. In either case, that instance, then, is detached from the context. To attach it again, you simply need to do:
context.Attach(category);
However, you don't have direct access to your context here. This is yet one more reason that you should never use the repository pattern with EF. So much pain and suffering has been subjected on developers throughout the year by either bad advice or erroneously attempting to do things as they are used to.
EF is an ORM (object relational mapper), which is a fancy way of saying that it is itself a data layer. The DbContext is the unit of work and each DbSet is a repository... already. The repository pattern is for abstracting low-level database access (i.e. all the crud of constructing SQL strings, for example). EF is already a high-level abstraction, trying to cram it into another repository pattern layer only cripples it and leads to problems like what you're experiencing here.
Long and short, the issue is that category is detached. You need to either ensure that it never becomes detached in the first place (i.e. don't use AsNoTracking for example) or find a way to ensure that it's reattached later. However, your best bet here is to throw away all this repository garbage completely and just use the context directly. Choosing to use an ORM like EF is simply choosing to use a third-party DAL, rather than write your own. Writing your own, anyways, on top of that is just wrong. You use the built in routing framework in ASP.NET Core. You use the built in templating engine (i.e. Razor). Do you feel the need to put some abstraction around those? Of course not, so why is a DAL any different? If you simply must create an abstraction, then use a meaningful one such as CQRS, service layer, or microservices patterns.

DDD and Entity Framework classes

I have read many articles about DDD and understood, that I should use my domain model classes in the Infrastructure level, so, I should use the same classes as Entity Framework infrastructure and use them to generate tables (code-first approach) etc. But my domain model can be fully different than Relational DB model.
Why I can't create one more model, infrastructure model, to create relational DB model and don't mix domain model with EF classes?
Consider this simple example:
Domain Model
public class Customer
{
public Customer(IRegistrar registrar)
{
this.registrar = registrar;
}
public int Age
{
get
{
// Just for this example. This will not work for all locals etc but beyond the point here.
var today = DateTime.Today;
return today.Year - this.DateOfBirth.Year;
}
}
public DateTime DateOfBirth { get; set; }
public int Register()
{
if (this.Age < 18)
{
throw new InvalidOperationException("You must be at least 18 years old");
}
int id = this.registrar.Register(this);
return id;
}
}
public interface IRegistrar
{
public int Register(Customer customer);
}
A lot of people when they do not have a domain model will do this in an MVC controller:
public ActionResult Search(Customer customer)
{
var today = DateTime.Today;
var age = today.Year - this.DateOfBirth.Year;
if (age < 18)
{
// Return an error page or the same page but with error etc.
}
// All is good
int id = this.registrar.Register(customer);
// The rest of code
}
There are a few issues with that:
What if the developer forgets to make the check for age before calling registrar? Many people will say, well that is a bad developer. Well whatever the case is, this type of code is prone to bugs.
The product is doing well so CFO decides to open up the API because there are many developers out there who are making great UI interfaces for customer registration and they want to use our API. So the developers go ahead and create a WCF service like this:
public int Register(Customer customer)
{
var today = DateTime.Today;
var age = today.Year - this.DateOfBirth.Year;
if (age < 18)
{
// Return a SOAP fault or some other error
}
int id = this.registrar.Register(customer);
// The rest of code
}
Now the developers can forget to make the check for age in 2 different places.
The code is also in 2 different places. If there is a bug, we need to remember to fix it in 2 different places.
If the company starts operating in places where the legal age is 21, we need to find all the places and add this rule.
If we are discussing the rules with BA, well we need to look through all the applications and find the rules.
In the above case we only have one rule: Age must be greater than 18. What if we had many more rules and many more classes? You can see where this will go.
EF Model
Your EF model may be like this:
public class Customer
{
public int Id { get; set; }
public DateTime DateOfBirth { get; set; }
// It may have a foreign key etc.
}
Application Layer Model
And your model for MVC view maybe like this:
public class Customer
{
// Or instead of Domain.Customer, it may be a CustomerDto which is used
// to transfer data from one layer or tier to another.
// But you get the point.
public Customer(Domain.Customer customer)
{
this.DateOfBirth = customer.DateOfBirth;
this.Age = customer.Age;
if (this.DateOfBirth.DayOfYear == DateTime.Today.DayOfYear)
{
this.Greeting = "Happy Birthday!!!";
}
}
public int Age { get; set; }
[Required(ErrorMessage = "Date of birth is required.")]
[Display(Name = "Data of birth")]
public DateTime DateOfBirth { get; set; }
public string Greeting { get; set; }
}
Here is a question: How many EF models have you seen with the Display attribute? I will let you decide if the EF model should concern itself with how it is displayed in the UI. Just the assumption that my EF model will be displayed in UI is wrong. Maybe the only consumers of my class is another web service. I don't think Display should be in the EF model but some may not agree with me; you make the call.
There are loads of questions on stackoverflow about people asking that sometime PropertyX is required and sometimes it is not, how can I do this? Well if you did not put Required attribute on your EF model and use your EF model in your view, then you would not have this issue. There will be one model for the view where PropertyX is a required field. That model will decorate PropertyX with the Required attribute, while another model for the view that does not require PropertyX will not decorate the property with the Required attribute.
ViewModels
And then you may have a viewmodel for a customer for a WPF application and you may have a javascript viewmodel for the frontend (KnockoutJS viewmodel).
Conclusion and answer to your question
So in conclusion, you can have different domain models than your entity models. Your domain model should be unaware of the database. If you decide to remove a column from one table due to normalization and put it into a table of its own, your entity model will be affected. Your domain model should not be affected.
I have read arguments on the net such as "this design takes too long, I just want to roll something out quickly and give it to the client and get paid". Well if you are not designing a product which will need to be maintained and features will be added to it but you are just designing a quick little site for your client then do not use this approach. No design applies to every situation. The point to take away is that your design should be chosen wisely with future in mind.
Also the conversion from entity model to domain to a model for MVC does not need to be done manually. There are libraries out there which will do this for you easily such as AutoMapper.
But I have to admit, there are tons of examples on the net and also in use in many applications where the entity models are used throughout the application and rules are implemented everywhere with loads of if statements.
Relation of this to DDD
When I read your question, I find something that catches the eye. It is this:
I have read many articles about DDD and understood, that I should use my domain model classes in the Infrastructure level, so, I should use the same classes as Entity Framework infrastructure and use them to generate tables (code-first approach)
To be honest, the best source of DDD knowledge it still the Blue Book. I know, I know, it is thick and hard to read. May be have a look at DDD Distilled by Vernon. The conclusion should be that DDD is not really about dealing with persistence but in deeper insight of the domain, better understanding your domain experts. Definitely, it says nothing about ORM.
Domain Model persistence
Domain models usually consist of objects (if we talk about object-oriented models) with state and behaviour. A model would have one or more entities and may be some value objects. In many cases you only have one entity per bounded context. Entities are grouped in Aggregates, that change together, forming transaction boundaries. This means that each change within the Aggregate is one transaction, no matter how many entities this change touches. Each Aggregate has one and only one entity, the Aggregate Root, which exposes public methods for others to work with the whole Aggregate.
So your Repository should take care of:
Persisting the whole Aggregate(no matter how many entities are there) within one transaction, for new and updated objects
Fetching the whole Aggregate from your persistence store, by its identity (Aggregate Root Id property)
You for sure will need some Queries but they can query how they want as soon as they do not amend the domain model state. Many add querying methods to the Repository but it is up to you. I would implement them as a separate static class with DbContext extension methods.
Models not matching each other
You mentioned that your persistence model does not match the domain model. This might be the case although for many situations it is not the case. There are a few ways of dealing with this:
Keep state separate of the behaviour and have it as a property in the domain object. Like Order with AddLine and so on, and OrderState with all these Total, CustomerId and stuff like this. Bear in mind that this might not work nice for complex aggregates.
Concentrate on the two main methods of the Repository that I mentioned above - Add and Get. Each Repository works for one type of Aggregate only and how you map between them is up to you.
Combined with the point above, you can reconsider using ORM and do something else. Basically you can just use ADO.NET but the easiest is to use some sort of document-oriented stuff like NoSQL although many would disagree. Check also this article about PostgreSQL JSONB storage as persistence.
Remember that the main point is to have the Repository that will do the work for you and potentially (probably this would never happen but still) use another store.
You might also be interested in another Vernon's article where he discusses using EF specifically.

How To Model Aggregates and Persist to Database in DDD

I am just trying to get out of my comfort zone of typical N-Tier architecture of Repository/Service/Presentation and have started looking at DDD with Aggregates and I have to admit I'm a little confused and was hoping someone could clarify the following example:
If I had an Entity called News, NewsImage and Customer which were all EF persist-able objects like so:
public class Customer
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
public class NewsImage
{
public virtual int Id { get; set; }
public virtual byte[] Data { get; set; }
public virtual News News { get; set; }
}
public class News
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<NewsImage> NewsImages { get; set; }
public virtual Customer Customer { get; set; }
}
As I understand it these could be the objects we would use to persist the domain objects to the database but if we are using aggregates from the domain model we could have something like this:
public class NewsAggregate
{
public int Id { get; set; }
public string Name { get; set }
public void AddImageToNews(byte[] imageData)
{
// Hide NewsImage or that object and add the byte[] data here?
}
}
My questions are following and I would appreciate any clarification as I am certain I am misunderstanding the fundamental principles here:
Only the aggregate objects should be exposed to the presentation layer (or any consuming layer).
How do I handle converting/persisting the aggregate objects to the database, I could use mapping which is fine but then how do I know if I am creating an object or updating (by whether it is transient if the Id is set or not?). How do I know if new images have been added and which to update or remove? I think the problem I am having is I call create pass a news aggregate to a Repository and create it, I could then get the aggregate back from the domain populated via the entities with EF and then add an image, when I pass the news aggregate back how do I know what has changed in order to create/update data?
Where should the customer go, should it be on the news aggregate object as an AddCustomer method, should there be a CustomerAggregate which has an AddNews method and with both of these options how to persist?
Many thanks for any insight, I've been reading around and looking at sample projects which demonstrate the concept but don't seem to fully explain best ways to achieve this.
First: DDD does not suggest you any specific architecture. I've used many different architectures with DDD and you should use what's good for the task. Obviously, if you think in a data driven way, you will encounter many problems with DDD.
DDD is a methodology designed to cope with complex business rules. You should not use it if your application value is in technological asset (as being in the cloud, exposing web services or some nice html5/mobile UI), but in the complexity of the business that it handles.
You should not use DDD for simple business rules. The rule of thumbs is: if you don't need a domain expert to understand the business, you don't need DDD at all.
Then, to properly understand aggregates, you should read the Vernon's essay on the topic.
That essay explain that aggregates exist to ensure business invariants.
You should never use aggregates just to optimize db access.
1) It depends on what capacity. There is a rule stating that aggregates can only reference other aggregates directly - not entities or value objects contained in other aggregates. This is to enforce aggregates as consistency boundaries - they fully encapsulate what they "aggregate". There should be a repository per aggregate. The presentation layer, and any outer layer, can require references to aggregates in two general capacities - for display purposes or for behavioral purposes. An aggregate shouldn't concern itself too much with how it will be displayed because queries can be implemented using a different model better suited for the task - a read-model. Instead, the aggregate should focus on behavior. And yes, in cases where the presentation layer wishes to execute a behavior on an aggregate it should reference the aggregate by its identity. Better yet, create an application service to encapsulate the domain layer and expose the behaviors as a simple facade.
Also, an aggregate is not a single class but usually a set of classes clustered around an aggregate root which is an entity. You don't necessarily need a separate class to represent the aggregate, it could just be the root entity.
2) For persistence, it seems you're using EF which should handle all change tracking for you. It should keep track of which objects are persistent or which are transient. ORMs such as NHibernate also do this.
2.1) This depends on whether Customer is itself an aggregate. If so, then News should reference Customer by ID only. Moreover, it may be that a customer is required for a news entity in which case a customer ID should be passed to the constructor of the News entity. If it is not required, then there is a behavior which associated a customer with a news entity. Consider this from the domain perspective - what is the meaning of associating a customer with a news entity? Try to move away from thinking in a technical, CRUD manner such as AddCustomer and think more in terms of the surrounding business intent.
As pointed out by Giacomo Tesio, DDD shows its value in domains with some complexity in business logic. If all your behaviors can be mapped to CRUD then leave it CRUD. Otherwise, look for behaviors in your domain instead of focusing on the data. Your entities and value objects should expose behaviors and hide state as much as possible. Do read and re-read the referenced article: Effetive Aggregate Design by Vaughn Vernon.

How should you handle parent and child entities in the repository pattern?

I'm working on making a image site and I'm wondering how I should structure the Create/Update methods in my Linq to Sql repository?
I have a Photo entity that is structured like:
public class Image
{
public int ID { get; set; }
public int OwnerId { get; set; }
public string Url { get; set; }
public IEnumerable<Tag> Tags { get; set; }
}
I have the following tables: Images, Tags, ImageTag
Now, I'm wondering when I call my CreateImage method in my ImageRepository, should I also be creating the Tags in the Tags table and the making the mappings in the ImageTag table?
Likewise, when I call UpdateImage, should I be checking if the tags have changed, and if they have then deleting entries in the ImageTag table and adding in different ones (as well as possibly adding new Tags to the tags table)?
Or should both of these use cases happen in a separate repository call?
Basically, how is this parent/child, one-to-many relationship handled typically in a typical linq to sql repository?
I would say it is not the responsibility of the repository to understand the relationships between the entities in your model. After all, if you were to change the way you persist your data (to a no-sql solution for example, or even if you just change your db schema), your entities and your model shouldn't change.
So your best bet is to have separate repository calls, and wrap the calls inside a unit of work. (A Unit of Work is basically an abstracted out transaction).
Your unit of work and the various calls to the repository would be a layer of abstraction above the repository. I don't know the rest of your model, so that could be different things. It could be a method on the Image object. You could have a Mediator. Or, if you are approaching this from a DDD perspective, a Domain Service.
Cheers.

Categories

Resources