CQRSã«ã€ããŠåããŠèããã®ã¯ãæ°ããä»äºãåŸããšãã§ãã ç§ãä»æ¥ãŸã§åããŠããäŒç€Ÿã¯ãCQRSãEvent Sourcingãããã³MongoDBããç§ãåãçµããããžã§ã¯ãã®ããŒã¿ããŒã¹ãšããŠäœ¿çšãããŠãããšããã«èšãããŸããã ããããã¹ãŠãããMongoDBã«ã€ããŠã®ã¿èããã CQRSãæãäžããŠã¿ããšããã®ã¢ãããŒãã®è€éããããã«ã¯ç解ã§ããŸããã§ããããäœããã®çç±ã§ãããŒã¿çžäºäœçšã¢ãã«ãèªã¿åããšæžã蟌ã¿ã®2ã€ã«åå²ããã¢ã€ãã¢ã奜ãã§ããã ããããããããè·åã®åé¢ãããã°ã©ãã³ã°ãã©ãã€ã ãšå
±é³Žããã®ã¯ãããããDDDã®ç²Ÿç¥ã«éåžžã«å¿ å®ã ã£ãããã§ãããã
äžè¬çã«ãå€ãã®äººããã¶ã€ã³ãã¿ãŒã³ãšããŠCQRSã«ã€ããŠèªã£ãŠããŸãã ç§ã®æèŠã§ã¯ãããã¯åã«ãèšèšãã¿ãŒã³ããšåŒã°ããã«ã¯ãã¢ããªã±ãŒã·ã§ã³ã®å
šäœçãªã¢ãŒããã¯ãã£ã«åœ±é¿ãäžãããããååãŸãã¯ã¢ãããŒããšåŒã¶ããšã奜ã¿ãŸãã CQRSã䜿çšãããšãã¢ããªã±ãŒã·ã§ã³ã®ã»ãŒãã¹ãŠã®é
ã
ãŸã§æµžéããŸãã
ããã«æ確ã«ãããã®ã¯ãCQRS + Event Sourcingã®æã ãã§äœæ¥ããããšã§ããCQRSã ããè©Šããããšã¯ãããŸãããEventSourcingãªãã§ã¯å€ãã®å©ç¹ã倱ãããããã«æããããã§ãã äŒæ¥ã®Paralect.DomainãCQRSãã¬ãŒã ã¯ãŒã¯ãšããŠäœ¿çšããŸãã 圌ã¯ä»ã®äººãããæªã人ãããåªããŠããã ãããã«ããŠããç§ã¯ããªããæ®ãã«ç²Ÿéããããšãå§ããŸãã ããã§ã¯ãããã€ãã®.NETãã¬ãŒã ã¯ãŒã¯ã«ã€ããŠã®ã¿èšåããŸãã æã人æ°ã®ããã®ã¯ã
NCQRS ã
Lokad CQRS ã
SimpleCQRSã§ãã ãŸããèšå€§ãªæ°ã®ç°ãªãããŒã¿ããŒã¹ããµããŒãããJonathan Oliver
Event StoreãèŠãããšãã§ããŸãã
CQRSããå§ããŸããã
CQRSãšã¯äœã§ããïŒ
CQRSã¯Command Query Responsibility Segregationã®ç¥ã§ãã ããã¯ãã°ã¬ãã°ã€ã³ã°ããæåã«èãããã¶ã€ã³ãã¿ãŒã³ã§ãã ããã¯ãããŸããŸãªã¢ãã«ã䜿çšããŠæ
å ±ãæŽæ°ããã³èªã¿åãããšãã§ãããšããåçŽãªæŠå¿µã«åºã¥ããŠããŸãã ãã ãããã®åçŽãªæŠå¿µã¯ãæ
å ±ã·ã¹ãã ã®èšèšã«é倧ãªçµæããããããŸãã ïŒcïŒããŒãã£ã³ã»ãã¡ãŠã©ãŒ
網çŸ
çãªå®çŸ©ãšã¯èšããŸããããä»åºŠã¯ãã¡ãŠã©ãŒãäœã念é ã«çœ®ããŠãããã説æããããšããŸãã
ãããŸã§ã®ãšãããäºå®äžãã¹ãŠã®äººãCRUDãªããžããªãšåæ§ã«ããŒã¿ã¢ãã«ãæäœãããããªç¶æ³ãçºçããŸããã CQRSã¯ä»£æ¿ã¢ãããŒããæäŸããŸãããããŒã¿ã¢ãã«ã ãã§ãªã圱é¿ãäžããŸãã CQRSã䜿çšããå Žåãããã¯ã¢ããªã±ãŒã·ã§ã³ã®ã¢ãŒããã¯ãã£ã«å€§ãã圱é¿ããŸãã
ããã¯ç§ãCQRSãããŒãã£ãŒããæããæ¹æ³ã§ã
æåã«ç®ãåŒãã®ã¯ãèªã¿åãçšïŒã¯ãšãªïŒãšæžã蟌ã¿çšïŒã³ãã³ãïŒã®2ã€ã®ããŒã¿ã¢ãã«ãæ¢ã«ããããšã§ãã éåžžãããã¯2ã€ã®ããŒã¿ããŒã¹ãæã£ãŠããããšãæå³ããŸãã ãããŠãCQRS + Event Sourcingã䜿çšããŠãããããæžã蟌ã¿ããŒã¹ïŒæžã蟌ã¿ã¢ãã«ïŒã¯ãã¹ãŠã®ãŠãŒã¶ãŒã¢ã¯ã·ã§ã³ã®ãã°ã®ãããªã€ãã³ãã¹ãã¢ã§ãïŒå®éããã¹ãŠã§ã¯ãªããããžãã¹ã¢ãã«ã®èŠ³ç¹ããéèŠãªã¢ã¯ã·ã§ã³ã®ã¿ïŒèªã¿åãããŒã¹ã®æ§ç¯ã«åœ±é¿ããŸãïŒã ãŸããèªã¿åãããŒã¹ã¯éåžžããŠãŒã¶ãŒã«è¡šç€ºããå¿
èŠãããããŒã¿ã®éæ£èŠåãªããžããªã§ãã èªã¿åãããŒã¹ãéæ£èŠåãããŠãããšèšã£ãã®ã¯ãªãã§ããïŒ ãã¡ãããä»»æã®ããŒã¿æ§é ãèªã¿åãã¢ãã«ãšããŠäœ¿çšã§ããŸãããCQRS + Event Sourcingã䜿çšããå Žåã¯ããã€ã§ãå®å
šã«åæ§ç¯ã§ãããããèªã¿åãããŒã¹ã®æ£èŠåã«ããŸãç
©ããã¹ãã§ã¯ãªããšæããŸãã ç¹ã«ããªã¬ãŒã·ã§ãã«ããŒã¿ããŒã¹ã䜿çšããã«NoSQLã䜿çšããå Žåã¯ãããã¯å€§ããªãã©ã¹ã«ãªããŸãã
æžã蟌ã¿ããŒã¹ã¯éåžžãã€ãã³ãã®1ã€ã®ã³ã¬ã¯ã·ã§ã³ã§ãã ã€ãŸãããªã¬ãŒã·ã§ãã«ããŒã¿ããŒã¹ã䜿çšããŠãæå³ããããŸããã
ã€ãã³ããœãŒã·ã³ã°
ã€ãã³ããœãŒã·ã³ã°ã®èåŸã«ããèãæ¹ã¯ãã¢ããªã±ãŒã·ã§ã³ã®ç¶æ
ãããŒã¿ããŒã¹ã«å€æŽãããã¹ãŠã®ã€ãã³ããèšé²ããããšã§ãã ãããã£ãŠããšã³ãã£ãã£ã®ç¶æ
ã§ã¯ãªãããšã³ãã£ãã£ã«é¢é£ãããã¹ãŠã®ã€ãã³ããä¿åããããšãããããŸãã ããããç§ãã¡ã¯ç¶æ
ãæ£ç¢ºã«æäœããããšã«æ
£ããŠãããããŒã¿ããŒã¹ã«ä¿åãããŠããããã€ã§ãèŠãããšãã§ããŸãã
ã€ãã³ããœãŒã·ã³ã°ã®å Žåããšã³ãã£ãã£ã®ç¶æ
ãæäœããŸãã ãã ããéåžžã®ã¢ãã«ãšã¯ç°ãªãããã®ç¶æ
ã¯ä¿åãããåŒã³åºããã³ã«åçããŸãã
ããŒã¿ããŒã¹ãããŠããããçæããã³ãŒããèŠããšãåŸæ¥ã®ã¢ãããŒããšã®éãã«æ°ä»ããªããããããŸããã
var user = Repository.Get<UserAR>(userId);
å®éããªããžããªã¯ããŒã¿ããŒã¹ããUserARã¢ã°ãªã²ãŒãã®æºåå®äºç¶æ
ãååŸããïŒAR = Aggregate RootïŒãããŒã¿ããŒã¹ãããã®ãŠãŒã¶ãŒã«é¢é£ä»ããããŠãããã¹ãŠã®ã€ãã³ããéžæããã¢ã°ãªã²ãŒãã®OnïŒïŒã¡ãœããã«æž¡ãããã«ããããåçããŸãã
ããšãã°ãUserARéèšã¯ã©ã¹ã«ã¯ããŠãŒã¶ãŒç¶æ
ã§IDã埩å
ããããã«æ¬¡ã®ã¡ãœãããå¿
èŠã§ãã
protected void On(User_CreatedEvent created) { _id = created.UserId; }
ãŠãããã®ç¶æ
å
šäœããããŠãŒã¶ãŒã®_idã®ã¿ãå¿
èŠãªã®ã§ããã¹ã¯ãŒããååãªã©ã®ç¶æ
ã埩å
ã§ããŸãã ãã ãããããã®ãã£ãŒã«ãã¯ãããããUser_CreatedEventã ãã§ãªããä»ã®ã€ãã³ãã«ãã£ãŠå€æŽããããšãã§ããŸãããã¹ãŠãåŠçããå¿
èŠããããŸãã ãã¹ãŠã®ã€ãã³ãã¯é çªã«åçãããããããã®ç¶æ
ãå€æŽãããã¹ãŠã®ã€ãã³ãã®OnïŒïŒãã³ãã©ãŒãèšè¿°ããŠããªãéããåžžã«éçŽã®ææ°ã®çŸåšã®ç¶æ
ã§äœæ¥ããããšã確信ããŠããŸãã
CQRS + Event Sourcingã®åäœæ¹æ³ããŠãŒã¶ãŒã«äœæããäŸãèŠãŠã¿ãŸãããã
ããŒã ã®äœæãšéä¿¡
æåã«ããããšã¯ãããŒã ãçµæããŠéãããšã§ãã ç°¡åã«ããããã«ããŠãŒã¶ãŒäœæã³ãã³ãã«å¿
èŠãªãã£ãŒã«ãã®ã¿ãèšå®ãã次ã®ããã«ããŸãã
public class User_CreateCommand: Command { public string UserId { get; set; } public string Password { get; set; } public string Email { get; set; } }
ããŒã ã®ã¯ã©ã¹åãæ··åããªãã§ãã ãããäžè¬çã«åãå
¥ããããŠããã¬ã€ãã©ã€ã³ã«å¯Ÿå¿ããŠããŸããããæå±ãããŠããããšå®è¡ãããŠããã¢ã¯ã·ã§ã³ãããã«ç解ã§ããŸãã
var command = new User_CreateCommand { UserId = â1â, Password = âpasswordâ, Email = âtest@test.comâ, }; command.Metadata.UserId = command.UserId; _commandService.Send(command);
次ã«ããã®ã³ãã³ãã®ãã³ãã©ãŒãå¿
èŠã§ãã ã³ãã³ããã³ãã©ã¯ãå¿
èŠãªéçŽã®IDãæž¡ãå¿
èŠããããŸãããã®IDã«ãã£ãŠããªããžããªããéçŽãåãåããŸãã ãªããžããªã¯æ¬¡ã®ããã«éçŽãªããžã§ã¯ããæ§ç¯ããŸãããã®éçŽã«é¢é£ãããã¹ãŠã®ã€ãã³ããããŒã¿ããŒã¹ããååŸããæ°ãã空ã®éçŽãªããžã§ã¯ããäœæããéçŽãªããžã§ã¯ãã§åä¿¡ããã€ãã³ããé çªã«åçããŸãã
ãã ããäœæããŒã ããããããããŒã¿ããŒã¹ãã調éãããã®ã¯ãããŸãããã€ãŸããèªåã§éèšãäœæããããã«ã³ãã³ãã¡ã¿ããŒã¿ãæž¡ããŸãã
public class User_CreateCommandHandler: CommandHandler<User_CreateCommand> { public override void Handle(User_CreateCommand message) { var ar = new UserAR(message.UserId, message.Email, message.Password, message.Metadata); Repository.Save(ar); } }
ãŠãããèšèšè
ãã©ã®ããã«èŠãããèŠãŠã¿ãŸãããã
public UserAR(string userId, string email, string password, ICommandMetadata metadata): this() { _id = userId; SetCommandMetadata(metadata); Apply(new User_CreatedEvent { UserId = userId, Password = password, Email = email }); }
ãŸããã¢ã°ãªã²ãŒãã«ã¯ãã©ã¡ãŒã¿ãŒã®ãªãã³ã³ã¹ãã©ã¯ã¿ãŒãå¿
èŠã§ããããã¯ããªããžããªãã¢ã°ãªã²ãŒãã®ç¶æ
ãåçŸãããšãã«ãæåã«ãã¹ã䜿çšããŠã€ã³ã¹ã¿ã³ã¹ãäœæãã次ã«ã€ãã³ããæ圱ã¡ãœããã«æž¡ãå¿
èŠãããããã§ãïŒ
Onã¡ãœãã
ïŒUser_CreatedEvent createdïŒã¯æ圱ã¡ãœããã®1ã€ã§ãïŒã
æ圱ã«ã€ããŠå°ã説æããŸãã ãããžã§ã¯ã·ã§ã³ã¯ããã®å®è£
ã«é¢é£ããã€ãã³ãã¹ãã¢ããã®ã€ãã³ãã«åºã¥ããŠãå®è£
ã®ç¶æ
ãåçŸãããã®ã§ãã ãŠãŒã¶ãŒã®äŸã§ã¯ããããã¯ãã¹ãŠãã®ç¹å®ã®ãŠãŒã¶ãŒã®ã€ãã³ãã§ãã ãããŠãApplyã¡ãœãããä»ããŠä¿åããããã¹ãŠã®åãã€ãã³ãã§ããéçŽã¯ããã®ç¶æ
ã®åçäžã«åŠçã§ããŸãã ãã¬ãŒã ã¯ãŒã¯ã§ã¯ããã®ããã«Onã¡ãœããïŒ/ * EventType arg * /ïŒãèšè¿°ããã ãã§ååã§ããããã§ãEventTypeã¯åŠçããã€ãã³ãã®ã¿ã€ãã§ãã
éçŽã®Applyã¡ãœããã¯ããã¹ãŠã®ãã³ãã©ãŒãžã®ã€ãã³ãã®éä¿¡ãããªã¬ãŒããŸãã å®éãã€ãã³ãã¯ãã¢ã°ãªã²ãŒãããªããžããªã«ä¿åããããšãã«ã®ã¿ãã£ã¹ããããããApplyã¯åã«ã€ãã³ããã¢ã°ãªã²ãŒãã®å
éšãªã¹ãã«è¿œå ããŸãã
ãŠãŒã¶ãŒèªèº«ã®èªã¿åãããŒã¹ã«æžã蟌ããŠãŒã¶ãŒãäœæããããã®ã€ãã³ããã³ãã©ãŒïŒïŒïŒã次ã«ç€ºããŸãã
public void Handle(User_CreatedEvent message) { var doc = new UserDocument { Id = message.UserId, Email = message.Email, Password = message.Password }; _users.Save(doc); }
ã€ãã³ãã«ã¯è€æ°ã®ãã³ãã©ãŒãå«ããããšãã§ããŸãã ãã®ã¢ãŒããã¯ãã£ã¯ãããŒã¿ã倧å¹
ã«éæ£èŠåãããå Žåã«ããŒã¿ã®æŽåæ§ãç¶æããã®ã«åœ¹ç«ã¡ãŸãã ãŠãŒã¶ãŒã®ç·æ°ãé »ç¹ã«è¡šç€ºããå¿
èŠããããšããŸãã ãããããããã®æ°ãå€ããããããããŒã¿ããŒã¹ã«å¯Ÿããã«ãŠã³ãæäœã¯éåžžã«é«äŸ¡ã§ãã ãã®åŸãæ¢ã«çµ±èšãåç
§ããå¥ã®ã€ãã³ããã³ãã©ãäœæãããŠãŒã¶ãŒãè¿œå ãããã³ã«ãåèšãŠãŒã¶ãŒã«ãŠã³ã¿ãŒã1ãã€å¢ãããŸãããããŠãã«ãŠã³ã¿ãŒãæŽæ°ããã«ãŠãŒã¶ãŒãäœæããªãããã«ããŸãã CQRSã䜿çšããŠããªãã£ãããéåžžã®ORMã䜿çšããŠããå Žåã䜿çšããããã«è¿œå ããã³åé€ãããã¹ãŠã®å Žæããã©ã£ãŠãã«ãŠã³ã¿ãŒãæŽæ°ãããããã«ããå¿
èŠããããŸãã
ãŸããã€ãã³ããœãŒã·ã³ã°ã䜿çšãããšãããã«ã¡ãªããããããŸãã EventHandlerã§ãã¹ãããããå¿
èŠãªå Žæã§ã€ãã³ããåŠçããªãã£ãå Žåã§ããæ£ããããžãã¹ããžãã¯ã§èªã¿åãããŒã¿ããŒã¹ããªãã€ã¬ã¯ãããã ãã§ç°¡åã«ä¿®æ£ã§ããŸãã
äœæã§ç解ããããšãã§ããŸãã éèšå€æŽãšããŒã æ€èšŒã¯ã©ã®ããã«è¡ãããŸããïŒ ãã¹ã¯ãŒãå€æŽã®äŸãèããŠã¿ãŸãããã
ä»ã®å Žæã§ã¯äžè¬çã«éãã¯ããã»ã©å€§ãããªããããã³ãã³ããã³ãã©ãšChangePasswordïŒïŒéçŽã¡ãœããã®ã¿ãæäŸããŸãã
ã³ãã³ããã³ãã©ãŒ
public class User_ChangePasswordCommandHandler: IMessageHandler<User_ChangePasswordCommand> {
éçŽã«ãŒã
public class UserAR : BaseAR {
ç¡å¹ãªã€ãã³ããApplyïŒïŒã¡ãœããã«æž¡ãããšã¯ãå§ãã§ããŸããã ãã¡ãããã€ãã³ããã³ãã©ã§åŸã§åŠçããããšãã§ããŸãããéèŠã§ã¯ãªãå Žåã¯ã€ãã³ãã¹ãã¢ãè©°ãŸãããã ããªã®ã§ããŸã£ããä¿åããªãæ¹ãè¯ãã§ãããã
ãã¹ã¯ãŒãã®å€æŽã®å Žåããã¡ãã倱æãããã¹ã¯ãŒãã®å€æŽã«é¢ããçµ±èšãåéããªãéãããã®ã€ãã³ããä¿åããããšã¯æå³ããããŸããã ãããŠããã®å Žåã§ãããã®ã€ãã³ããæžã蟌ã¿ã¢ãã«ã§èªåã«ãšã£ãŠåœ¹ã«ç«ããªãã®ãããããšããã³ãã¹ãã¬ãŒãžã«æžã蟌ãã®ãçã«ããªã£ãŠããããæ
éã«æ€èšããå¿
èŠããããŸãã ã€ãã³ãæ€èšŒã®ããžãã¹ããžãã¯ãå€æŽãããå¯èœæ§ããããšæãããå Žåã¯ãä¿åããŸãã
å®éããã®èšäºã§èª¬æãããããšã¯ããã ãã§ãã ãã¡ãããCQRS +ã€ãã³ããœãŒã·ã³ã°ã®ãã¹ãŠã®åŽé¢ãšå¯èœæ§ãæããã«ããããã§ã¯ãããŸãããããã«ã€ããŠã¯ä»åŸã®èšäºã§èª¬æããäºå®ã§ãã ãã®ã¢ãããŒãã䜿çšããéã«çºçããåé¡ãèåŸã«æ®ã£ãŠããŸãã ãããŠãããã«ã€ããŠã話ããŸãã
質åãããå Žåã¯ãã³ã¡ã³ãã§è³ªåããŠãã ããã åãã§ãçãããŸãã ãŸãã次ã®èšäºã«äœãææ¡ãããã°-æ¬åœã«èãããã§ãã
ãœãŒã¹
ASP.NET MVCã®å®å
šã«æ©èœããäŸã¯
ãã¡ãã§ãã
ããŒã¿ããŒã¹ã¯ååšããããã¹ãŠãã¡ã¢ãªã«ä¿åãããŸãã å¿
èŠã«å¿ããŠããã蟌ãã ãã§ååã§ãã ãŸããã€ãã³ããä¿åããããã®MongoDBã®ã€ãã³ãã¹ãã¢ã®æ¢è£œã®å®è£
ãããã«äœ¿çšã§ããŸãã
Global.asaxãã¡ã€ã«ã«çµã¿èŸŒãã«ã¯ãInMemoryTransitionRepositoryãMongoTransitionRepositoryã«çœ®ãæããã ãã§ååã§ãã
èªã¿åãã¢ãã«ãšããŠãéçã³ã¬ã¯ã·ã§ã³ããããããåèµ·åãããã³ã«ããŒã¿ãç Žå£ãããŸãã
次ã¯ïŒ
ãã®ãããã¯ã«é¢ããèšäºã«ã€ããŠããã€ãã®ã¢ã€ãã¢ããããŸãã ãã£ãšææ¡ããã æãèå³æ·±ããã®ãèšããŸãã
- ã¹ãããã·ã§ãããšã¯äœã§ããããªãå¿
èŠãªã®ãã詳现ãšå®è£
ãªãã·ã§ã³ã
- ã€ãã³ãã¹ãã¢ã
- ããŒã¿ããŒã¹ã®åçæã æ©äŒã ããã©ãŒãã³ã¹ã®åé¡ã 䞊ååã ãããã
- éçŽã«ãŒãã«ãã£ãŠèšèšãããŠããŸãã
- å®éã®ãããžã§ã¯ããžã®é©çšã 1ã€ã®ãããžã§ã¯ãã¯å€éšå§èšããã2çªç®ã®ãããžã§ã¯ãã¯ç§ã®ã¹ã¿ãŒãã¢ããã§ãã
- ãµãŒãããŒãã£ãµãŒãã¹ã®çµ±åã®æ©èœã