Entity persitance inside Domain Events using a rep

2019-05-23 08:12发布

问题:

I am delving into domain events and need some advice about persisting updates to an entity for history reasons. My example deals with a User entity and Signing In:

    public class UserService
    {
        private UserRepository _repository;

        public UserService()
        {
            _repository = new UserRepository();
        }

        public User SignIn(string username, string password)
        {
            var user = _repository.FindByUsernameAndPassword(username, password);
            //As long as the found object is valid and an exception has not been thrown we can raise the event.
            user.LastLoginDate = DateTime.Now;
            user.SignIn();
            return user;
        }
    }

    public class User
    {
        public User(IEntityContract entityContract)
        {
            if (!entityContract.IsValid)
            {
                throw new EntityContractException;
            }
        }

        public Guid Id { get; set; }
        public string Username { get; set; }
        public string Password { get; set; }
        public DateTime LastLoginDate { get; set; }

        public void SignIn()
        {
            DomainEvent.Raise(new UserSignInEvent() {User = this});
        }
    }

    public class UserSignInEvent : IDomainEvent
    {
        public User User { get; set; }
    }


    public class UserSignInHandler : Handles<UserSignInEvent>
    {
        public void Handle(UserSignInEvent arguments)
        {
            //do the stuff
        }
    }

So where I have the do the stuff, I want to update the User object LastLoginDate and possibly log the date and time the user logged in for historical reasons. My question is, would I create a new instance of my repository and context to save the changes in the handler or pass something into the Event? This is what I am struggling with right now.

回答1:

So where I have the do the stuff, I want to update the User object LastLoginDate and possibly log the date and time the user logged in for historical reasons.

Remembering last login date should be concern of user itself.
You already have nice extension point - user has signIn method.

My question is, would I create a new instance of my repository and context to save the changes in the handler or pass something into the Event?

User shouldn't know anything about entity framework.
Therefore - User.Events shouldn't know anything either.
Domain event handlers shouldn't know too.

Those handlers that live "outside" (e.g. in application layer) are allowed to.
But they would figure out entity framework context from elsewhere and not from user or events if necessary.


As I see it - events here are necessary for logging functionality only.

I would write something like this:

public class LoginService{
   private Users _users;
   public LoginService(Users users){
     _users = users;
   }         
   public User SignIn(string username, string password){
     var user = _users.ByUsernameAndPassword(username, password);
     user.SignIn();
     return user;
   }
 }

public class User{
   public DateTime LastLoginDate { get; set; }      
   public void SignIn(){
     LastLoginDate = DateTime.Now;
     Raise(new SignedIn(this));
   }
   public class SignedIn:DomainEvent<User>{
     public SignedIn(User user):base(user){}
   }
}

//outside of domain model
public class OnUserSignedIn:IEventHandler<User.SignedIn>{
  public void Handle(User.SignedIn e){
    var u=e.Source;
    var message="User {0} {1} logged in on {1}"
      .With(u.Name,u.LastName,u.LastLoginDate);
    Console.WriteLine(message);
  }
}

Bad thing about this code is that service method is command and query simultaneously
(it modifies state and returns result).

I would resolve that with introducing UserContext which would be notified that user has signed in.

That would make need for returning signed in user unnecessary,
responsibility of serving current user would be shifted to UserContext.

About repository and updating Your user - I'm pretty sure entity framework is smart enough to know how to track entity state changes. At least in NHibernate - only thing I'm doing is flushing changes when httprequest finishes.