Cross Entity Action Implementation

by TheCatWhisperer   Last Updated December 10, 2017 02:05 AM

I have a domain layer, which my application architecture holds "sacred"; that is to say, the domain layer has no references to either data storage, or presentation concerns (vice-versa is allowed). I would like to keep it this way!

Everything was going quite well until I came to one particular requirement. Given:

public class A
{
     public string IdA { get; private set; }
     public void DoWork()
}

public class B
{
    public string IdB { get; private set; }
    public string IdA { get; private set; }
    public Invaidate()
}

Under certain uncommon conditions, which A decides, when DoWork is called all associated B must be invalidated. The problem, this invalidation requires persistence. Note: this edge case is the only time A would need to be concerned with its associated Bs. The requirement for this invalidation would not be obvious to a consumer of A

I have come up with, and implemented, a solution (1), as well as thought of other solutions. However, I am not quite happy with it, or any of the others, and would like input.

Solution 1: A Invalidates Bs, Calls Delegate For Persistence

Advantage: Transparent to consumer, Easy to consume

Disadvantage: Strange and inconsistent save behavior, no other class in the domain layer saves when a method is called

class A
{
    private Action<B> SaveB; // Populated by ARepository when instance is retrieved, delegate points to sister repo BRepository
    public List<B> Bees { get; private set; }
    public DoWork()
    {
        // if edgeCase:
        foreach (B b in Bees)
        {
            b.Invalidate();
            SaveB(b);
        }
    }
 }

Solution 2: A Invalidates Bs, A's Repository Saves Associated Bs

Advantage: Transparent to consumer, Easy to consume, Consistent Domain Layer

Disadvantage: Inconsistent persistent layer, Monolithic Persistence Antipattern*

*Perhaps the wrong anti-pattern name?

class A
{
    public List<B> Bees { get; private set; }
    public DoWork()
    {
        // if edgeCase:
        foreach (B b in Bees)
            b.Invalidate();
    }
}
//...
class ARepository
{
     Update(A a)
     {
         Sql.SaveA(a);
         bRepository.Update(a.Bees);
     }
}

Solution 3: Trigger Event On Edge Case

Advantage: No inconsistencies in domain or persistence layer behavior, No cross entity concerns

Disadvantage: Hard to consume, Exposes logic to consumer, who is supposed to be responsible for wiring up the event?!

class A
{
    public Event SaveBs/EdgeCaseHappened;
    public List<B> Bees { get; private set; }
    public DoWork()
    {
        // if edgeCase:
        foreach (B b in Bees)
            b.Invalidate();
        SaveBs.Fire();
       // or just...
       EdgeCaseHappened.Fire()
    }
}

While I have already implemented 1, one of these three solutions seem quite right to me, is there something here I am missing? Have I made a faulty assumption somewhere?

What is the best course of action for me too keep cross cutting concerns like this maintainable and usable, while strictly isolating my business logic?


Note: this is not a DDD project



Related Questions



Domain object using DAO within a transaction

Updated May 07, 2016 08:02 AM