When using MDA, should I distinguish between idempotent and non-idempotent event handlers?

The question involves using Event Sourcing.

When restoring the current state by replaying an event, event handlers should be idempotent. For example, when a user successfully updates his username, an event may occur UsernameUpdated, an event containing a row property newUsername. When restoring the current state, the corresponding event handler receives the event UsernameUpdatedand sets the property of the usernameobject Userto the property newUsernameof the event object UsernameUpdated. In other words, processing the same message several times always gives the same result.

However, how does such an event handler work when integrated with external services? For example, if the user wants to reset their password, the object Usercan throw an event PasswordResetRequestedthat is processed by a part of the code that it issues to a third party with a command to send SMS. Now that the application is being rebuilt, we DO NOT want to resend this SMS. How to avoid this situation?

+1
source share
3 answers

Two messages are involved in the interaction: commands and events.

I do not view system messages in the messaging infrastructure in the same way as domain events. Processing a command message should be idempotent. Event handlers are usually not needed.

100 , :

public UserNameChanged ChangeUserName(string username, IServiceBus serviceBus)
{
    if (_username.Equals(username))
    {
        return null;
    }

    serviceBus.Send(new SendEMailCommand(*data*));

    return On(new UserNameChanged{ Username = userName});
}

public UserNameChanged On(UserNameChanged @event)
{
    _username = @event.UserName;

    return @event;
}

, . 100 UserNameChanged, , On . , , , .

, , .

+2

, . - , - . Axon

public class MyAggregateRoot extends AbstractAnnotatedAggregateRoot {

@AggregateIdentifier
private String aggregateIdentifier;
private String someProperty;

public MyAggregateRoot(String id) {
    apply(new MyAggregateCreatedEvent(id));
}

// constructor needed for reconstruction
protected MyAggregateRoot() {
}

@EventSourcingHandler
private void handleMyAggregateCreatedEvent(MyAggregateCreatedEvent event) {
    // make sure identifier is always initialized properly
    this.aggregateIdentifier = event.getMyAggregateIdentifier();
    // do something with someProperty
}

}

, , API .

-, , , , .

. Axon , , .

0

TL;DR; SMS .

"". idempotent, , , . "-", , .

, UUID ( ), , UUID . (a.k.a., "Sagas" ) , , , "". , "".

, , UUID SMS PasswordResetRequested. SMS , , .

( ++):


// The event indicating a password reset was successfully requested.
class PasswordResetRequested : public Event {
public:
    PasswordResetRequested(const Uuid& userUuid, const Uuid& smsUuid, const std::string& passwordResetCode);

    const Uuid userUuid;
    const Uuid smsUuid;
    const std::string passwordResetCode;
};

// The user aggregate root.
class User {
public:

    PasswordResetRequested requestPasswordReset() {
        // Realistically, the password reset functionality would have it own class 
        // with functionality like checking request timestamps, generationg of the random
        // code, etc.
        Uuid smsUuid = Uuid::random();
        passwordResetCode_ = generateRandomString();
        return PasswordResetRequested(userUuid_, smsUuid, passwordResetCode_);
    }

private:

    Uuid userUuid_;
    string passwordResetCode_;

};

// The process manager (aka, "saga") for handling password resets.
class PasswordResetProcessManager {
public:

    void on(const PasswordResetRequested& event) {
        if (!smsRepository_.hasSms(event.smsUuid)) {
            smsRepository_.queueSms(event.smsUuid, "Your password reset code is: " + event.passwordResetCode);
        }
    }

};

, :

-, () , UUID SMS , , .

  • . , "bob" reset, SMS UUID "1234", (, 2 ) "" reset, UUID "1234" ", SMS, , , .

  • . UUID, SMS, "bob" , "" SMS-, . UUID , , "" reset "bob" .

-, UUID SMS , User PasswordResetProcessManager ( PasswordResetManager), . , User , SMS, , SMS . User , , SmsQueued , PasswordResetRequested , , SMS, reset , reset .

-, PasswordResetRequested, , PasswordResetProcessManager SMS, SMS , PasswordResetRequested ( ). , "" .


( , , OrderProcessManager: https://msdn.microsoft.com/en-us/library/jj591569.aspx). , , .

0

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


All Articles