
ãã¬ã¹ããŒãªãŒ
ç§ã®åã®èšäºã¯ãIMLïŒç§ãã¡ã®äž»åæ©èœïŒã§å§ãŸã£ãIncoding Frameworkã®çŽ¹ä»ã§ããã IMLã¯ãäŒæ¥ãããžã§ã¯ãã§äœ¿çšãããäžé£ã®ãŠãŒãã£ãªãã£ïŒéçºè ã®ããŒã ã®ãããªãã®ïŒä»¥äžã®ãããžã§ã¯ããéçºããããã«ç§ãã¡ãæŒããŸããããããã¯ä»ã®ã³ã³ããŒãã³ããæ©èœããªãããšãæå³ãããã®ã§ã¯ãããŸããããããã©ãããã磚ããããŠããããããããªãã«èšŒæããããšããŸãã
ç¹å¹è¬ïŒ
以åãç§ã¯åžžã«åãœãªã¥ãŒã·ã§ã³ã«é·æãšçæããããšãããµããŒã¿ãŒã§ããããCQRSã¯ç§ã®æèŠã§ã¯Nã¬ã€ã€ãŒãããåªããŠããããçŠå¿ãããå¯äœçšãããªããããã¯ãªããã®æåã®ã«ãŒããªããžã®åè£ã«ãªããŸãããããããŸãæåã«ã
誰ããCQRSã«ã€ããŠèããããšããããŸããïŒ
ãã§ã«CQRSã䜿çšããŠãã人ã«ãšã£ãŠã¯ãæåã®ã»ã¯ã·ã§ã³ã¯é¢çœããªããããããŸããããã®ããããbikeããšããã©ãã«ãä»ããåã«ã殺人æ©èœã»ã¯ã·ã§ã³ã«æ £ããŠããããšããå§ãããŸãã Nå±€ã¢ãŒããã¯ãã£ã䜿çšãã人ã¯ãCQRSãžã®åãæ¿ããæ€èšããå¿ èŠããããŸããç§ã®ææ¡ããµããŒãããããã«ãCQRSãžã®ãã¹ã説æããŸãã
ãããããã€ã®æœè±¡åã䟿å©ãïŒéå»ã®æèŠïŒ
ã¢ããªã±ãŒã·ã§ã³ã®éçºãå§ããã°ããã®ãšãã«ããµãŒããŒã¢ãŒããã¯ãã£ãšããŠN-LayerãéžæããŸãããããã«ãããã¢ããªã±ãŒã·ã§ã³ãå€æ°ã®ã¬ã€ã€ãŒã«åå²ããããããžã§ã¯ãã®ç°ãªãéšåãäºãã«ç¬ç«ããŠèšèšã§ããããã«ãªããŸããããæçµçã«æ¬¡ã®åé¡ãçºçããŸããïŒ
- ãœãŒã¹ã³ãŒãã®ãèšåŒµããåé¡ã¯ãå€ãã®å Žåããã¡ãµãŒãå±€ãéä¿¡å±€ãªã©ã®ã¿ã€å±€ã®è¿œå ãåå ã§çºçããŸãã
- è€æ°ã®ã¬ã€ã€ãŒã¬ãã«ã®èåŸã«è©³çŽ°ãé衚瀺ã«ããŸãã
泚ïŒN-Layerã®ã¢ã€ãã¢ã¯ãç¹å®ã®ã¬ã€ã€ãŒã®çœ®æDLLãšãé¢é£ããŠããŸããããã®éåžžã«ãŸããªã¿ã¹ã¯ã¯IoCã䜿çšããŠã¯ããã«ç°¡åã«è§£æ±ºã§ããŸãã
Nã¬ã€ã€ãŒã®ãæªãã®äž»ãªåå ã¯ã»ãšãã©ã®å ŽåããµãŒãã¹å±€ã§ãããµãŒãã¹å±€ã¯ãåæ§ã®ã¿ã¹ã¯ã1ã€ã®ã¯ã©ã¹ã«éçŽããããšã§ããžãã¹ããã»ã¹ãšã¢ããªã±ãŒã·ã§ã³ããžãã¯ã®äœæ¥ã®è©³çŽ°ãé ããŸãã
- å¯æ¥ã«é¢é£ããã¡ãœããã®ãµããŒããšæ¡åŒµ
- 倧ããªãªããžã§ã¯ãã§ãã¹ãããã®ã¯é£ãã
埮åŠã§ã¯ãªãå ŽåãCQRSã®æ¡ä»¶ä»ãå€æŽã¯ã倧ããªãªããžã§ã¯ããå°ããªãªããžã§ã¯ãã«åå²ããããšã§æ§æãããŸãã
public interface IUserService { void Add(string name); void Delete(string id); List<User> Fetch(Criteries criteries); }
å解åŸã2ã€ã®ã³ãã³ãïŒAddUserãDeleteUserïŒãšã¯ãšãªïŒFetchUserïŒãååŸããŸããããã«ãããã¯ã©ã¹ã®æ°ã¯å¢ããŸãããã¯ã©ã¹å ã®ããã€ãã®é¢é£ã¡ãœãããåãé€ãããšãã§ããŸãã
ãä»ãããã«å€ãã®ã¯ã©ã¹ãæžãå¿ èŠããããŸãïŒã-ããã¯Nã¬ã€ã€ãŒéçºè ããã®æåã®è³ªåã§ãããåè«ãšããŠãã¿ã¹ã¯ã®ã¢ãããã¯æ§ïŒãªããžã§ã¯ãéã®äŸåé¢ä¿ãªãïŒãååŸããããšãéžã³åºãããšãã§ããŸãã
- VCSïŒããŒãžã§ã³ç®¡çã·ã¹ãã ïŒã®ç«¶åãå°ãªã
- ã¯ã©ã¹æ€çŽ¢ã¯ã¡ãœãããããç°¡åã§ã
- UserServiceãéšåçã«åå²ããå¿ èŠããªããªããŸããã1ã€ãããã²ãŒãããã®ãé£ããããã§ãïŒ
- ã³ãã³ããšã¯ãšãªãã圢æããããã°ãã©ãã«ãŒã®åé¡
- ã¢ãžã£ã€ã«ã®ã¹ããªã³ãã¯ãã³ãã³ããšã¯ãšãªãã圢æãããŸã
- å°ããªãªããžã§ã¯ãã®ãã¹ã
ç§ãã¡ã®å®è£
ããã«ã³ãŒãããæãããããšæã£ãŠãããã£ãã¡ãªäººã®ããã«ã GitHubãããœãŒã¹ã³ãŒãïŒIncoding CQRS + MVDã®äŸããããŸãïŒãããŠã³ããŒãã§ããŸãã
泚ïŒãããžã§ã¯ããéå§ããã«ã¯ã空ã®ããŒã¿ããŒã¹ãäœæããweb.configã§ãã®ConnectionStringãæå®ããå¿ èŠããããŸãïŒããŒã¡ã€ã³ïŒ
ãã£ã¹ãããã£ãŒ
åäžã®ãã©ã³ã¶ã¯ã·ã§ã³ïŒäœæ¥åäœïŒã§ã¡ãã»ãŒãž ïŒ ã³ãã³ããŸãã¯ã¯ãšãª ïŒãå®è¡ããããŒèŠçŽ ã
ã³ã³ãããŒã©ãŒ -ãã£ã¹ãããã£ãŒã䜿çšããããã®asp.net mvcïŒã³ã³ãœãŒã«ãwpfãowinãªã©ïŒã·ã¹ãã ã®ãã¬ãŒã ã¯ãŒã¯ã§ã¯ãIoCããã€ã³ã¹ã¿ã³ã¹ãååŸããå¿ èŠãããã次ã®2ã€ã®ã¡ãœããã䜿çšã§ããŸãã
- ããã·ã¥-ã³ãã³ããå®è¡ããŸã
- ã¯ãšãª-ã¯ãšãªãå®è¡ããŸã
public ActionResult Test() { var dispatcher = IoCFactory.Instance.TryResolve<IDispatcher>(); var id = dispatcher.Query(new GetIdQuery()); dispatcher.Push(new DeleteEntityByIdCommand(id)); return something ActionResult; }
äœæ¥åäœ-Commandã®å®è£ ãèŠããšãã¡ã€ã³ã³ãŒãããªãŒããŒããŒããããExecuteã¡ãœããã«å«ãŸããŠããããšãããããŸããããã¯ãDispatcherã®åå ãªãã§åŒã³åºãããšãã§ããŸãããããŒã¿ããŒã¹ãšãã©ã³ã¶ã¯ã·ã§ã³ãžã®æ¥ç¶ã¯éãããŸããã
new DeactivateEntityCommand().Execute(); // without transaction and connection dispatcher.Push(new DeactivateEntityCommand()); // open transaction and connection
ã¡ãã»ãŒãž
CommandBaseãšQueryBaseã¯Messageã®åã§ããããã®åäœã¯äœæ¥åäœãäœæãããåé¢ã¬ãã«ã®ã¿ã€ããç°ãªããŸã
- ã³ãã³ã-ReadCommitted
- ã¯ãšãª-ReadUncommittedïŒèªã¿åãå°çšïŒ
泚ïŒå¶éã¯å³ããããã«æãããããããŸããããäœããã®çç±ã§ã¯ãšãªã«ããŒã¿ãä¿åïŒåé€ãæ¿å ¥ïŒããå¿ èŠãããå Žåã¯ãã¹ã¯ãªãããå°ããªã¿ã¹ã¯ã«åå²ããŠä¿®æ£ããå¿ èŠããããŸãã
ã¡ãã»ãŒãžã«ã¯2ã€ã®äž»èŠãªããŒã«ããããŸãã
ãªããžã㪠-ããŒã¿ããŒã¹ã€ã³ã¿ãŒãã§ã€ã¹ããã¹ãŠã®CRUDã¹ã¯ãªããããµããŒã
äŸ
泚ïŒã¯ãšãªïŒããŒãžåºåãïŒã¯ãæãåºç¯ãªãªããžããªã¡ãœããã§ãããã¯ãšãªä»æ§ã䜿çšããŠãååŸããããŒã¿ãèšè¿°ããŸãã
泚ïŒãããã€ããŒORMã远跡ããµããŒãããŠããªãå ŽåãRepository.SaveOrUpdateïŒãšã³ãã£ãã£ïŒã¡ãœãããåŒã³åºãå¿ èŠããããŸã
äœæãã
Repository.Save(new entity())
èªã
Repository.GetById<TEntity>(id); Repository.Query(whereSpecification: spec, orderSpecification:spec, paginatedSpecification:spec)
泚ïŒã¯ãšãªïŒããŒãžåºåãïŒã¯ãæãåºç¯ãªãªããžããªã¡ãœããã§ãããã¯ãšãªä»æ§ã䜿çšããŠãååŸããããŒã¿ãèšè¿°ããŸãã
æŽæ°ãã
var entityFromDb = Repository.GetById<TEntity>(id); entityFromDb.Title = "New title"; // tracking
泚ïŒãããã€ããŒORMã远跡ããµããŒãããŠããªãå ŽåãRepository.SaveOrUpdateïŒãšã³ãã£ãã£ïŒã¡ãœãããåŒã³åºãå¿ èŠããããŸã
åé€ãã
Repository.Delete<TEntity>(id);
ã€ãã³ããããŒã«ãŒ -ã³ãã³ãéã®éä¿¡ãããã«ããã ç¹°ãè¿ãçºçãããæçãã®ã³ãŒããéçŽããã€ãã³ããšãµãã¹ã¯ã©ã€ããŒãã«ãã»ã«åã§ããŸãã
ã¿ã¹ã¯ïŒããã€ãã®ã¢ã¯ã·ã§ã³ã®ç£æ»
åé¡ïŒç£æ»ãä¿åããããã®ã³ãŒãã¯åãã§ãããåã³ãã³ãã§ç¹°ãè¿ãå¿ èŠããããŸã
ãµãŒãã¹å±€ã®ãœãªã¥ãŒã·ã§ã³ïŒåºæ¬ã¯ã©ã¹ServiceWithAuditBaseãå²ãåœãŠãããšãã§ããŸãããç£æ»ã®è€éããå¢ããšç¶æãé£ãããªããç¶æ¿ã¯åžžã«è€éã«ãªããŸãã
å å ¥è ãœãªã¥ãŒã·ã§ã³
ã€ãã³ãã³ãŒã
public class OnAuditEvent : IEvent { public string Message { get; set; } }
泚ïŒã€ãã³ããIEventãå®è£ ããæ¡ä»¶
å å ¥è ã³ãŒã
public class AuditSubscriber : IEventSubscriber<OnAuditEvent> { readonly IRepository repository; public AuditSubscriber(IRepository repository) { this.repository = repository; } public void Subscribe(OnAuditEvent @event) { this.repository.Save(new Audit { Message = @event.Message }); } public void Dispose() { } }
泚ïŒãµãã¹ã¯ã©ã€ããŒã¯IoCFactoryãä»ããŠäœæããããããctorïŒã³ã³ã¹ãã©ã¯ã¿ãŒïŒã«æ³šå ¥ããããIoCFactory.Instance.TryResolveïŒïŒã䜿çšã§ããŸãã
ã³ãã³ãã³ãŒã
EventBroker.Publish(new OnAuditEvent { Message = "New product {0} by {1}".F(Title, Price) });
åãåãã
ã«ã¹ã¿ã ã¯ãšãªãäœæããã«ã¯ãQueryBaseãç¶æ¿ããå¿ èŠããããŸããããã§ãæåŸ ãããããŒã¿ãªã¿ãŒã³ãæå®ããExecuteResultã¡ãœããããªãŒããŒã©ã€ãããŸãã
public class GetProductsQuery : QueryBase<List<GetProductsQuery.Response>> { public class Response { public string Title { get; set; } public string Price { get; set; } } public string Title { get; set; } public decimal? From { get; set; } public decimal? To { get; set; } protected override List<Response> ExecuteResult() { return Repository.Query(whereSpecification: new ProductByTitleWhere(this.Title) .And(new ProductBetweenPriceWhere(this.From, this.To))) .Select(product => new Response { Title = product.Title, Price = product.Price.ToString("C") }) .ToList(); } }
ãã¹ããããã¯ã©ã¹ãçµæãšããŠäœ¿çšãããããšãåºå¥ã§ããŸããããªãããã§ã¯ãªãã®ã...
ããŒã¿ããŒã¹ïŒãšã³ãã£ãã£ïŒãããªããžã§ã¯ããããã«è¿ã -ãã®ã¡ãœããã«ã¯ãããŒã¿ããŒã¹ã«æ¥ç¶ããã»ãã·ã§ã³ã®ã¹ã³ãŒãã«é¢é£ããåé¡ããããŸããäŸãèããŠã¿ãŸãããã
äŸ
ã¯ãšãªã³ãŒã
ã³ã³ãããŒã©ãŒã³ãŒã
ã¯ãšãªãå®äºããåŸãã»ãã·ã§ã³ãéããããOrdersãã£ãŒã«ãã«ã¢ã¯ã»ã¹ãããšãªã¯ãšã¹ããããŒã¿ããŒã¹ã«éä¿¡ãããããããããã³ã°ORMã®ã¬ãã«ã§Lazy LoadïŒOLAPãªããžã§ã¯ãã®ã¿ã«é¢é£ããïŒããªãã«ããªããšããšã©ãŒãå®è¡æã«çºçããŸãã
return Repository.Query<Product>();
ã³ã³ãããŒã©ãŒã³ãŒã
dispatcher.Query(new GetProductsQuery()) .Select(r=> new { Amount = r.Orders.Sum(r=>r.Price))
ã¯ãšãªãå®äºããåŸãã»ãã·ã§ã³ãéããããOrdersãã£ãŒã«ãã«ã¢ã¯ã»ã¹ãããšãªã¯ãšã¹ããããŒã¿ããŒã¹ã«éä¿¡ãããããããããã³ã°ORMã®ã¬ãã«ã§Lazy LoadïŒOLAPãªããžã§ã¯ãã®ã¿ã«é¢é£ããïŒããªãã«ããªããšããšã©ãŒãå®è¡æã«çºçããŸãã
ViewModelã¯ãã¹ããããã¯ã©ã¹ãšåãã§ãããä»ã®ã¯ãšãªã§åå©çšããæ©èœããããŸãããããã¯éåžžã«ãŸããªã·ããªãªã§ãã
ã³ãã³ã
Command for CQRSã®æåã®å®è£ ã§ã¯ã説æïŒAddUserCommandïŒããšã°ãŒãã¥ãŒã¿ïŒUserCommandHandlerïŒããåé¢ããŸãããããã«ãããéçºããã»ã¹ãè€éã«ãªãããããã®éšåãããã«çµåãããŸããã
泚ïŒåé¢ã®äž»ãªçç±ã¯ãSOAPã·ã¹ãã ã®DTOïŒããŒã¿è»¢éãªããžã§ã¯ãïŒã¢ãã«ã®ãµããŒãã§ããããasp.net mvcã®åºçŸã«ãããé¢é£æ§ããªããªããŸããã
ã«ã¹ã¿ã ã³ãã³ããäœæããã«ã¯ãCommandBaseãç¶æ¿ããExecuteã¡ãœããããªãŒããŒã©ã€ãããå¿ èŠããããŸã
public class AddProductCommand : CommandBase { public string Title { get; set; } public decimal Price { get; set; } public override void Execute() { var product = new Product { Title = Title, Price = Price } Repository.Save(product); Result = product.Id; } }
泚ïŒã³ãã³ããããŒã¿ãè¿ãå¿ èŠãããã·ããªãªããããŸããã³ãã³ãã¡ãœããã«çµæãå ¥ããããšãã§ããŸã
ããªã³ã°æ©èœ
ã³ã³ããžãã
CQRSã¯ãè€éãªã¿ã¹ã¯ãå°ããªã¿ã¹ã¯ã«ãåå²ãããã®ã«åœ¹ç«ã¡ãŸãããå®è¡ãã©ã³ã¶ã¯ã·ã§ã³å šäœã«åé¡ããããŸãã
ã¿ã¹ã¯ ïŒ3段éã§ãªããžã§ã¯ããä¿åãã
解決ç ïŒ3ã€ã®ã³ãã³ãïŒStep1CommandãStep2CommandãStep3CommandïŒã«åå²ããŸã
æ¡ä»¶ ïŒãã©ã³ã¶ã¯ã·ã§ã³
public ActionResult Save(Step1Command step1,Step2Command step2,Step3Command step3) { dispatcher.Push(composite => { composite.Quote(step1); composite.Quote(step2); composite.Quote(step3); }); return IncodingResult.Success(); }
ã³ãã³ãã1ã€ã®ããã±ãŒãžã«ã°ã«ãŒãåããããšã«å ããŠãCompositeã§ã¯å®è¡çµæãæäœã§ããŸãã ã¿ã¹ã¯ãè€éã«ããå®è¡åŸã®Step1ãæ°ããèŠçŽ ã®IDãã¹ããã2ããã³3ã«æž¡ãããã«æ¡ä»¶ãèšå®ããŸãã
public ActionResult Save(Step1Command step1,Step2Command step2,Step3Command step3) { dispatcher.Push(composite => { composite.Quote(step1,new MessageExecuteSetting { OnAfter = () => { step2.Id = step1.Result; step3.Id = step1.Result; } }); composite.Quote(step2); composite.Quote(step3);}); }
ãããã¹ã¯ããæ¥ç¶æåå
èµ·åæã«ã¢ããªã±ãŒã·ã§ã³ãæ¥ç¶æååãåä¿¡ããªãããäœæ¥äžãªã©ãã·ã¹ãã ã«å ¥ã£ãåŸããµãŒãããŒãã£ã®ãµãŒãã¹ãçŸåšã®ã»ãã·ã§ã³ã®ã¢ãã¬ã¹ãçºè¡ããå Žåã以åã«æå®ãããã¹ãå€æŽã§ããå¿ èŠããããŸãã
dispatcher.Query(query, new MessageExecuteSetting { Connection = new SqlConnection(currentConnectionString) });
ã¬ã¬ã·ãŒã·ã¹ãã
ãå€ããããŒã¹ãã©ããããããšã¯ãIncoding Frameworkã®åé¡ã§ã¯ãããŸããã ããŸããŸãªæ§æã§äœæ¥ããããã®è¿œå ã®ã€ã³ãã©ã¹ãã©ã¯ãã£ã®äœæãé¿ãããã®è©³çŽ°ãèæ ®ããã«ã³ãã³ããšã¯ãšãªãéçºã§ããããŒã«ããããŸãã
dispatcher.Query(query, new MessageExecuteSetting { DataBaseInstance = "Instance 2" });
泚ïŒåããŒã«ã¯ç¬èªã®ORMèšå®ããããŸã
ãããã«
ãã®èšäºã¯äž»ã«Incoding CQRSã®å®è£ ã®ã¬ãã¥ãŒã«çŠç¹ãåœãŠãŠãããããCQRSæ¹æ³è«ã®ã¬ãã¥ãŒèªäœã¯ç°¡æœã§ãããä»ã®ãœãŒã¹ã§æ¢ã«ååã«èª¬æãããŠããŸãã CQRSã®ã³ãŒãã£ã³ã°ã¯ãã¬ãŒã ã¯ãŒã¯ã®äžéšã®1ã€ã§ãããå®å šã«èªçµŠèªè¶³ã§ãããä»ã®ã³ã³ããŒãã³ãïŒIMLãMVDããŠããããã¹ãïŒãªãã§é©çšãããŸãã
IMLã®æåã®èšäºãžã®ã³ã¡ã³ãã«ã¯ã代æ¿ïŒOWINïŒãã©ãããã©ãŒã ã§asp.net mvcã䜿çšããå¯èœæ§ã«ã€ããŠã®è³ªåããã£ããããIncoding CQRSãWPFãããžã§ã¯ãã§äœ¿çšãããããšãããã«ããããŸãã ã
PSã¬ãã¥ãŒãšã³ã¡ã³ããããã³ãã¬ãŒã ã¯ãŒã¯ã®åäœã«é¢ãã質åãèããŠããããã§ãã ã³ããŒã¢ã³ãããŒã¹ããšæŠã次ã®ã°ãªãŒã³ããŒããŒIncoding MVDã¯æ°é±é以å ã«ãããŸãïŒïŒïŒ