4

In our project we are following Domain Driven Design with entity framework. Recently i came upon one issue where i want to delete a object from collection . Lets say Customer have collection of purchase. I want to remove the particular purchase of one customer.

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

    public string Name { get; set; }

    public string Address { get; set; }

    public virtual ICollection<Purchase> Purchases { get; set; }
}

public class Purchase
{
    public int ID { get; set; }

    public int CustomerId { get; set; }

    public DateTime PurchaseDate { get; set; }
}

I tried to remove the purchase by

customer.Purchases.Remove(purchase);

but the above code removed only the relation not deleting the purchase from db . I want to remove them from db.

So to make that work i have given that responsibility to Repository and used context object inside repository to remove the purchase.

repository.DeletePurchase(purchase);

Is the above is right approach in Domain Driven Design or any possibilities to move the delete behavior within entity?

Pleas help me the follow the best practice to implement DDD.

Max_dev
  • 508
  • 7
  • 25
  • 2
    The best practice to implement DDD is to understand the Domain, then implement the delete exactly as the Domain does it. – MikeSW Apr 10 '14 at 15:30
  • Honestly i am not expert in DDD. From my understanding and from above code i can place the delete behaviour within entity itself but to do that i need object context which should not be placed inside entity.. so how to handle this.. hope u understood the question. The same can be achieved using domain events which i am trying – Max_dev Apr 10 '14 at 15:55
  • 1
    First of all you should understand what the domain understand by 'delete', chances are is a soft delete (marked as deleted). Implement that in your repository. Separation of Concerns still matters in DDD, so ask yourself if a behaviour really fits in the layer. Btw, an entity can't delete itself, we don't have suicides in code. You just need to think in a natural way using common sense, there are no secret techniques here. – MikeSW Apr 10 '14 at 16:02
  • Thanks, i am speaking about deleting the children from parent .. if you look my post u could understand.. so no suicide here :-) moreover i have used repository oly which u can see.. my question is very straight forward do we have any possibility to add that behavior in parent entity or using repository is ok... – Max_dev Apr 10 '14 at 16:08
  • 3
    I get the feeling your question is not about DDD but about EF. Your Customer class looks like an EF Entity, I doubt it represents the proper Customer concept. However the delete implementation usually belongs to the repository. I don't use EF so I can't help you with the delete command you want – MikeSW Apr 10 '14 at 16:31

3 Answers3

4

What is problematic to answer here is that I have no idea what behavior you're executing. "Removing the purchase of a customer" ... the more important question is why and when that's allowed/appropriate. Even then, purchases are somewhat of a transaction, what's the point of removing them (pretending they didn't happen, loosing history along the way). Your design is also problematic. What if I purchase something every day of the year? How many purchases will I need to load up just to remove that single purchase when we fast forward a year from now? How about modeling a purchase as a separate aggregate and just copy the id of the customer it is for? We can always find out all purchases of a customer using nothing but predicate on that customer id. Would that make sense in your context?

Time and time people forget to formulate the actual behavior they're trying to model, and adding proper context for the future reader is but a distant dream. Getting intimate with your domain and model are not optional.

Yves Reynhout
  • 2,982
  • 17
  • 23
  • 1
    Thanks mate, whatever you told is right. but the above is just an example. I need to implement delete logic in DDD way. – Max_dev Apr 10 '14 at 12:51
4

If you are following Domain Driven Design, an implementation of your collections should follow best practices (e.g. collection should be exposed as read-only, without setter, with AddPurchase/ DeletePurchase methods).

I assume that Purchase is not deleted because context.SaveChanges() is not invoked.

Unfortunately it also might be a known issue Entity cannot be deleted from child collection but can be from context.

EDIT : There are two options and all of them are not good enough.

  1. context.Set<Purchase>().Remove(purchase); can be invoked.
  2. Composite Primary key (Id + CustomerId) can be used for Purchase entity.

        modelBuilder.Entity<Purchase>()
            .HasRequired(it => it.Customer)
            .WithMany(it => it.Purchases)
            .HasForeignKey(it => it.CustomerId);
    
        modelBuilder.Entity<Purchase>().HasKey(it => new
            {
                Id = it.Id,
                CustomerId = it.CustomerId
            });
    

    By default HasKey() prevents identity. That's why it should be specified as attribute.

    public class Purchase
    {
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }
    
        public Customer Customer { get; set; }
    
        public int CustomerId { get; set; }
    
        public DateTime PurchaseDate { get; set; }
    }
    

There are some related discussions:

  1. Error when deleting one-to-many children
  2. A relationship is in the Deleted state
  3. Is it possible to remove child from collection and resolve issues on SaveChanges?
Community
  • 1
  • 1
Ilya Palkin
  • 14,687
  • 2
  • 23
  • 36
0

First of all, let it happen on the domain entities: if Purchases are only accessed via a collection on the Customer then just remove it from there (you are doing this now).

Secondly, something outside the domain need to ensure the Customer is correctly persisted back to the store, including deleting the Purchases. Something also needs to supplement your particular ORM's logic to achieve that. A couple of options for where you might do that:

  1. Since this an EF-specific concern, override the SaveChanges method in your context, trap the error, delete those purchases from the DB (or check which Purchases in the DB but are not in the in-memory context, then delete from DB)

  2. Or, in your application layer, which is responsible for the lifecycle of your Customer, you could track the purchase IDs prior to calling whatever service or entity method is going to remove those. Then when that has returned, you check which purchase IDs are now missing from the Customer, call the repository method to delete them, and call repository SaveChanges(). Bit nasty, because there might be lots of places where you remove a Purchase.

Chalky
  • 1,624
  • 18
  • 20