Aggregates vs. Data Models

The aggregate is requested from the storage as a whole and processed as a whole. It is recommended to create small units so as not to affect performance. And this part is quite complicated for me. Especially when it comes to persistent data.

I have property Activityc DueDate. An activity has Participantsthat can contribute to an PhasesActivity, but only until DueDate.
Therefore, every time a user contributes to a phase, I need to check whether he is a Member and Now < DueDate.
It seems that I do not need to download the entire activity graph with each participant, phase and contribution.

At the same time, I must limit the changes in the content of the phase if contributions to this phase already exist.

In addition to the fact that parallel transactions with contributions from different participants do not affect each other.

This gives me a hint about what ContributionToPhaseshould be a standalone aggregate and possibly referential aggregation of activity by id.
Although I still have to load activity aggregation to get the value of the DueDate property. And frankly, it bothers me a lot.

The data model is as follows:

Activity
------------
Id
Title
Description
DueDate
....

Phase
------------
Id
ActivityId
Order
Title
Description
....


ContributionToPhase
------------
Id
PhaseId
ParticipantId
....

You can see that in the Data Model there is no direct relationship between Activityand ContributionToPhase. If I developed it as a Transaction Script, I would create a special DTO that carries all the data (but no more) needed to verify a specific transaction:

ContributionRelatedDTO
    Id
    ActivtyId
    PhaseId
    UserId
    ActivityDueDate
    TimeStamp
    ....

or

PhaseContentsRelatedDTO
    Id
    ActivtyId
    HasContributions
    Timestamp
    ....

DDD? , ContributionToPhase DueDate, Activity? ?

+4
1

DDD ORM, CQRS. DDD, , , , DDD , , , , .

. CommandHandler CommandData. a CommandData, DTO. . CommandHandler , -, . :

public interface ICommandHandler<T>
{

    T Handle(T command);

}

public class ContributeToPhaseCommandData
{

    public Guid ContributionToPhaseId { get; set; }
    public Guid ActivityId { get; set; }
    public Guid PhaseId { get; set; }
    public Participant Contributor { get; set; }
    public DateTime ActivityDueDate { get; set; }

    public bool Success { get; set; }
    public string CommandResultMessage { get; set; }


    public ContributeToPhaseCommandData( /* mandatory data in constructor */ ) { }

}

public class ContributeToPhaseCommandHandler : ICommandHandler<ContributeToPhaseCommandData>
{

    public ContributeToPhaseCommandHandler( /* inject other services, if needed */ )
    {

    }

    public ContributeToPhaseCommandData Handle(ContributeToPhaseCommandData command)
    {
        // do stuff here, you might set some response data in the 'command' and return it.
       // You might send a DomainEvent here, if needed.
        return command;
    }

}

Application Layer, - (- ). , ( ), , ( ), , requester (. ) .

, ? , , . , .

, agreggates, / . , / , , . .

, . , lighter , - ( ).

, (KISS) :

public SomeResponseToCaller ContributeToPhase(ICommandHandler<ContributeToPhaseCommandData> command, Guid phaseId, IPrincipal caller, IAuthorizationService authorizer)
{
    if (!authorizer.authorizes(caller))
        this.ExceptionHandler.Handle("Caller is not authorized! Shall we log this info?");
    using(var db = new ActivitiesContext())
    {
        ContributeToPhaseCommandData data = db.Phases
            .Select(p => new ContributeToPhaseCommandData()
                {
                    ActivityId = p.ActivityId,
                    PhaseId = p.Id,
                    Contributor = p.Activity.Participants.SingleOrDefault(part => part.Name == caller.Identity.Name)
                    ActivityDueDate = p.Activity.DueDate
                }).SingleOrDefault(p => p.Id == phaseId);

        if (data == null)
            this.ExceptionHandler.Handle("Phase not found");

        if (data.Contributor == null)
            this.ExceptionHandler.Handle("Caller is not a participant of this Activity!!!!");

        data.ContributionToPhaseId = Guid.NewGuid();

        var result = command.Handle(data);

        db.SaveChanges();

        return new SomeResponseToCaller() {
            Success = result.Success,
            ContributionId = result.ContributionToPhaseId,
            Message = result.CommandResultMessage
        };
    }
}

ExceptionHandler - , IExcepionHandler, . Application. , AuthorizationService .

, .

CQRS. , , , Querying from Storing. :

... - , , , . , , CQRS .

, , - , . . DTO/ViewModels.

, , , , , -, " " . , /, , .

, , , . , , .

, NoSQL, ( ), . , , , .

, , , (, SQL EF). , . ​​ , , CQRS, , , . , - ().

0

Source: https://habr.com/ru/post/1623109/


All Articles