Äú¿ÉÒÔ¾èÖú£¬Ö§³ÖÎÒÃǵĹ«ÒæÊÂÒµ¡£

1Ôª 10Ôª 50Ôª





ÈÏÖ¤Â룺  ÑéÖ¤Âë,¿´²»Çå³þ?Çëµã»÷Ë¢ÐÂÑéÖ¤Âë ±ØÌî



  ÇóÖª ÎÄÕ ÎÄ¿â Lib ÊÓÆµ iPerson ¿Î³Ì ÈÏÖ¤ ×Éѯ ¹¤¾ß ½²×ù Modeler   Code  
»áÔ±   
 
   
 
 
     
   
 ¶©ÔÄ
  ¾èÖú
dz̸ÃüÁî²éѯְÔð·ÖÀë(CQRS)ģʽ
 
×÷Õߣºº®½­¶Àµö À´Ô´£º²©¿ÍÔ° ·¢²¼ÓÚ 2015-10-29
  3519  次浏览      27
 

ÔÚ³£ÓõÄÈý²ã¼Ü¹¹ÖУ¬Í¨³£¶¼ÊÇͨ¹ýÊý¾Ý·ÃÎʲãÀ´Ð޸ĻòÕß²éѯÊý¾Ý£¬Ò»°ãÐ޸ĺͲéѯʹÓõÄÊÇÏàͬµÄʵÌå¡£ÔÚһЩҵÎñÂß¼­¼òµ¥µÄϵͳÖпÉÄÜûÓÐʲôÎÊÌ⣬µ«ÊÇËæ×ÅϵͳÂß¼­±äµÃ¸´ÔÓ£¬Óû§Ôö¶à£¬ÕâÖÖÉè¼Æ¾Í»á³öÏÖһЩÐÔÄÜÎÊÌâ¡£ËäÈ»ÔÚDBÉÏ¿ÉÒÔ×öһЩ¶Áд·ÖÀëµÄÉè¼Æ£¬µ«ÔÚÒµÎñÉÏÈç¹ûÔÚ¶Áд·½Ãæ»ìºÏÔÚÒ»ÆðµÄ»°£¬ÈÔÈ»»á³öÏÖһЩÎÊÌâ¡£

±¾ÎĽéÉÜÁËÃüÁî²éѯְÔð·ÖÀëģʽ(Command Query Responsibility Segregation£¬CQRS)£¬¸Ãģʽ´ÓÒµÎñÉÏ·ÖÀëÐÞ¸Ä (Command£¬Ôö£¬É¾£¬¸Ä£¬»á¶Ôϵͳ״̬½øÐÐÐÞ¸Ä)ºÍ²éѯ£¨Query£¬²é£¬²»»á¶Ôϵͳ״̬½øÐÐÐÞ¸Ä)µÄÐÐΪ¡£´Ó¶øÊ¹µÃÂß¼­¸ü¼ÓÇåÎú£¬±ãÓÚ¶Ô²»Í¬²¿·Ö½øÐÐÕë¶ÔÐÔµÄÓÅ»¯¡£ÎÄÕÂÊ×ÏȼòÒª½éÉÜÁË´«Í³µÄCRUD·½Ê½´æÔÚµÄÎÊÌ⣬½Ó׎éÉÜÁËCQRSģʽ£¬×îºóÒÔÒ»¸ö¼òµ¥µÄÔÚÏßÈÕ¼ÇϵͳÑÝʾÁËÈçºÎʵÏÖCQRSģʽ¡£ÒªÌ¸µ½¶Áд²Ù×÷£¬Ê×ÏÈÎÒÃÇÀ´¿´´«Í³µÄCRUDµÄÎÊÌâ¡£

Ò» CRUD·½Ê½µÄÎÊÌâ

ÔÚÒÔǰµÄ¹ÜÀíϵͳÖУ¬ÃüÁî(Command£¬Í¨³£ÓÃÀ´¸üÐÂÊý¾Ý£¬²Ù×÷DB)ºÍ²éѯ(Query)ͨ³£Ê¹ÓõÄÊÇÔÚÊý¾Ý·ÃÎʲãÖÐRepositoryÖеÄʵÌå¶ÔÏó(ÕâЩ¶ÔÏóÊǶÔDBÖбíµÄÓ³Éä)£¬ÕâЩʵÌåÓпÉÄÜÊÇSQLServerÖеÄÒ»ÐÐÊý¾Ý»òÕß¶à¸ö±í¡£

ͨ³£¶ÔDBÖ´ÐеÄÔö£¬É¾£¬¸Ä£¬²é£¨CRUD£©¶¼ÊÇÕë¶ÔµÄϵͳµÄʵÌå¶ÔÏó¡£Èçͨ¹ýÊý¾Ý·ÃÎʲã»ñÈ¡Êý¾Ý£¬È»ºóͨ¹ýÊý¾Ý´«Êä¶ÔÏóDTO´«¸ø±íÏֲ㡣»òÕߣ¬Óû§ÐèÒª¸üÐÂÊý¾Ý£¬Í¨¹ýDTO¶ÔÏó½«Êý¾Ý´«¸øModel£¬È»ºóͨ¹ýÊý¾Ý·ÃÎʲãд»ØÊý¾Ý¿â£¬ÏµÍ³ÖеÄËùÓн»»¥¶¼ÊǺÍÊý¾Ý²éѯºÍ´æ´¢Óйأ¬¿ÉÒÔÈÏΪÊÇÊý¾ÝÇý¶¯£¨Data-Driven£©µÄ£¬ÈçÏÂͼ£º

¶ÔÓÚһЩ±È½Ï¼òµ¥µÄϵͳ£¬Ê¹ÓÃÕâÖÖCRUDµÄÉè¼Æ·½Ê½Äܹ»Âú×ãÒªÇó¡£ÌرðÊÇͨ¹ýһЩ´úÂëÉú³É¹¤¾ß¼°ORMµÈÄܹ»·Ç³£·½±ã¿ìËÙµÄʵÏÖ¹¦ÄÜ¡£

µ«ÊÇ´«Í³µÄCRUD·½·¨ÓÐһЩÎÊÌ⣺

ʹÓÃͬһ¸ö¶ÔÏóʵÌåÀ´½øÐÐÊý¾Ý¿â¶Áд¿ÉÄÜ»áÌ«´Ö²Ú£¬´ó¶àÊýÇé¿öÏ£¬±ÈÈç±à¼­µÄʱºò¿ÉÄÜÖ»ÐèÒª¸üиö±ð×ֶΣ¬µ«ÊÇÈ´ÐèÒª½«Õû¸ö¶ÔÏó¶¼´©½øÈ¥£¬ÓÐЩ×Ö¶ÎÆäʵÊDz»ÐèÒª¸üеġ£ÔÚ²éѯµÄʱºòÔÚ±íÏÖ²ã¿ÉÄÜÖ»ÐèÒª¸ö±ð×ֶΣ¬µ«ÊÇÐèÒª²éѯºÍ·µ»ØÕû¸öʵÌå¶ÔÏó¡£

ʹÓÃͬһʵÌå¶ÔÏó¶ÔͬһÊý¾Ý½øÐжÁд²Ù×÷µÄʱºò£¬¿ÉÄÜ»áÓöµ½×ÊÔ´¾ºÕùµÄÇé¿ö£¬¾­³£Òª´¦ÀíµÄËøµÄÎÊÌ⣬ÔÚдÈëÊý¾ÝµÄʱºò£¬ÐèÒª¼ÓËø¡£¶ÁÈ¡Êý¾ÝµÄʱºòÐèÒªÅжÏÊÇ·ñÔÊÐíÔà¶Á¡£ÕâÑùʹµÃϵͳµÄÂß¼­ÐԺ͸´ÔÓÐÔÔö¼Ó£¬²¢ÇÒ»á¶ÔϵͳÍÌÍÂÁ¿µÄÔö³¤»á²úÉúÓ°Ïì¡£

ͬ²½µÄ£¬Ö±½ÓÓëÊý¾Ý¿â½øÐн»»¥ÔÚ´óÊý¾ÝÁ¿Í¬Ê±·ÃÎʵÄÇé¿öÏ¿ÉÄÜ»áÓ°ÏìÐÔÄܺÍÏìÓ¦ÐÔ£¬²¢ÇÒ¿ÉÄÜ»á²úÉúÐÔÄÜÆ¿¾±¡£

ÓÉÓÚͬһʵÌå¶ÔÏó¶¼»áÔÚ¶Áд²Ù×÷ÖÐÓõ½£¬ËùÒÔ¶ÔÓÚ°²È«ºÍȨÏ޵ĹÜÀí»á±äµÃ±È½Ï¸´ÔÓ¡£

ÕâÀïÃæºÜÖØÒªµÄÒ»¸öÎÊÌâÊÇ£¬ÏµÍ³ÖеĶÁдƵÂʱȣ¬ÊÇÆ«Ïò¶Á£¬»¹ÊÇÆ«Ïòд£¬¾ÍÈçͬһ°ãµÄÊý¾Ý½á¹¹ÔÚ²éÕÒºÍÐÞ¸ÄÉÏʱ¼ä¸´ÔӶȲ»Ò»Ñù£¬ÔÚÉè¼ÆÏµÍ³µÄ½á¹¹Ê±Ò²ÐèÒª¿¼ÂÇÕâÑùµÄÎÊÌâ¡£½â¾ö·½·¨¾ÍÊÇÎÒÃǾ­³£Óõ½µÄ¶ÔÊý¾Ý¿â½øÐжÁд·ÖÀë¡£ ÈÃÖ÷Êý¾Ý¿â´¦ÀíÊÂÎñÐÔµÄÔö£¬É¾£¬¸Ä²Ù×÷(Insert,Update,Delete)²Ù×÷£¬ÈôÓÊý¾Ý¿â´¦Àí²éѯ²Ù×÷(Select²Ù×÷)£¬Êý¾Ý¿â¸´ÖƱ»ÓÃÀ´½«ÊÂÎñÐÔ²Ù×÷µ¼Öµıä¸üͬ²½µ½¼¯ÈºÖеĴÓÊý¾Ý¿â¡£ÕâÖ»ÊÇ´ÓDB½Ç¶È´¦ÀíÁ˶Áд·ÖÀ룬µ«ÊÇ´ÓÒµÎñ»òÕßϵͳÉÏÃæ¶ÁºÍдÈÔÈ»ÊÇ´æ·ÅÔÚÒ»ÆðµÄ¡£ËûÃǶ¼ÊÇÓõÄͬһ¸öʵÌå¶ÔÏó¡£

Òª´ÓÒµÎñÉϽ«¶ÁºÍд·ÖÀ룬¾ÍÊǽÓÏÂÀ´Òª½éÉܵÄÃüÁî²éѯְÔð·ÖÀëģʽ¡£

¶þ ʲôÊÇCQRS

CQRS×îÔçÀ´×ÔÓÚBetrand Meyer£¨EiffelÓïÑÔÖ®¸¸£¬¿ª-±ÕÔ­ÔòOCPÌá³öÕߣ©ÔÚ Object-Oriented Software Construction Õâ±¾ÊéÖÐÌáµ½µÄÒ»ÖÖ ÃüÁî²éѯ·ÖÀë (Command Query Separation,CQS) µÄ¸ÅÄî¡£Æä»ù±¾Ë¼ÏëÔÚÓÚ£¬ÈκÎÒ»¸ö¶ÔÏóµÄ·½·¨¿ÉÒÔ·ÖΪÁ½´óÀࣺ

1.ÃüÁî(Command):²»·µ»ØÈκνá¹û(void)£¬µ«»á¸Ä±ä¶ÔÏóµÄ״̬¡£

2.²éѯ(Query):·µ»Ø½á¹û£¬µ«ÊDz»»á¸Ä±ä¶ÔÏóµÄ״̬£¬¶ÔϵͳûÓи±×÷Óá£

¸ù¾ÝCQSµÄ˼Ï룬ÈκÎÒ»¸ö·½·¨¶¼¿ÉÒÔ²ð·ÖΪÃüÁîºÍ²éѯÁ½²¿·Ö£¬±ÈÈ磺

private int i = 0;
private int Increase(int value)
{
i += value;
return i;
}

Õâ¸ö·½·¨£¬ÎÒÃÇÖ´ÐÐÁËÒ»¸öÃüÁî¼´¶Ô±äÁ¿i½øÐÐÏà¼Ó£¬Í¬Ê±ÓÖÖ´ÐÐÁËÒ»¸öQuery£¬¼´²éѯ·µ»ØÁËiµÄÖµ£¬Èç¹û°´ÕÕCQSµÄ˼Ï룬¸Ã·½·¨¿ÉÒÔ²ð³ÉCommandºÍQueryÁ½¸ö·½·¨£¬ÈçÏ£º

private void IncreaseCommand(int value)
{
i += value;
}
private int QueryValue()
{
return i;
}

²Ù×÷ºÍ²éѯ·ÖÀëʹµÃÎÒÃÇÄܹ»¸üºÃµÄ°ÑÎÕ¶ÔÏóµÄϸ½Ú£¬Äܹ»¸üºÃµÄÀí½âÄÄЩ²Ù×÷»á¸Ä±äϵͳµÄ״̬¡£µ±È»CQSÒ²ÓÐһЩȱµã£¬±ÈÈç´úÂëÐèÒª´¦Àí¶àÏ̵߳ÄÇé¿ö¡£

CQRSÊǶÔCQSģʽµÄ½øÒ»²½¸Ä½ø³ÉµÄÒ»ÖÖ¼òµ¥Ä£Ê½¡£ ËüÓÉGreg YoungÔÚCQRS, Task Based UIs, Event Sourcing agh! ÕâÆªÎÄÕÂÖÐÌá³ö¡£¡°CQRSÖ»ÊǼòµ¥µÄ½«Ö®Ç°Ö»ÐèÒª´´½¨Ò»¸ö¶ÔÏó²ð·Ö³ÉÁËÁ½¸ö¶ÔÏó£¬ÕâÖÖ·ÖÀëÊÇ»ùÓÚ·½·¨ÊÇÖ´ÐÐÃüÁÊÇÖ´ÐвéѯÕâÒ»Ô­ÔòÀ´¶¨µÄ(Õâ¸öºÍCQSµÄ¶¨ÒåÒ»ÖÂ)¡±¡£

CQRSʹÓ÷ÖÀëµÄ½Ó¿Ú½«Êý¾Ý²éѯ²Ù×÷(Queries)ºÍÊý¾ÝÐ޸IJÙ×÷(Commands)·ÖÀ뿪À´£¬ÕâÒ²Òâζ×ÅÔÚ²éѯºÍ¸üйý³ÌÖÐʹÓõÄÊý¾ÝÄ£ÐÍÒ²ÊDz»Ò»ÑùµÄ¡£ÕâÑù¶ÁºÍдÂß¼­¾Í¸ôÀ뿪À´ÁË¡£

ʹÓÃCQRS·ÖÀëÁ˶ÁдְÔðÖ®ºó£¬¿ÉÒÔ¶ÔÊý¾Ý½øÐжÁд·ÖÀë²Ù×÷À´¸Ä½øÐÔÄÜ£¬¿ÉÀ©Õ¹ÐԺͰ²È«¡£ÈçÏÂͼ£º

Ö÷Êý¾Ý¿â´¦ÀíCUD£¬´Ó¿â´¦ÀíR£¬´Ó¿âµÄµÄ½á¹¹¿ÉÒÔºÍÖ÷¿âµÄ½á¹¹ÍêȫһÑù£¬Ò²¿ÉÒÔ²»Ò»Ñù£¬´Ó¿âÖ÷ÒªÓÃÀ´½øÐÐÖ»¶ÁµÄ²éѯ²Ù×÷¡£ÔÚÊýÁ¿ÉÏ´Ó¿âµÄ¸öÊýÒ²¿ÉÒÔ¸ù¾Ý²éѯµÄ¹æÄ£½øÐÐÀ©Õ¹£¬ÔÚÒµÎñÂß¼­ÉÏ£¬Ò²¿ÉÒÔ¸ù¾ÝרÌâ´ÓÖ÷¿âÖл®·Ö³ö²»Í¬µÄ´Ó¿â¡£´Ó¿âÒ²¿ÉÒÔʵÏÖ³ÉReportingDatabase£¬¸ù¾Ý²éѯµÄÒµÎñÐèÇ󣬴ÓÖ÷¿âÖгéȡһЩ±ØÒªµÄÊý¾ÝÉú³ÉһϵÁвéѯ±¨±íÀ´´æ´¢¡£

ʹÓÃReportingDatabaseµÄһЩÓŵãͨ³£¿ÉÒÔʹµÃ²éѯ±äµÃ¸ü¼Ó¼òµ¥¸ßЧ£º

1.ReportingDatabaseµÄ½á¹¹ºÍÊý¾Ý±í»áÕë¶Ô³£ÓõIJéѯÇëÇó½øÐÐÉè¼Æ¡£

2.ReportingDatabaseÊý¾Ý¿âͨ³£»áÈ¥Õý¹æ»¯£¬´æ´¢Ò»Ð©ÈßÓà¶ø¼õÉÙ±ØÒªµÄJoinµÈÁªºÏ²éѯ²Ù×÷£¬Ê¹µÃ²éѯ¼ò»¯ºÍ¸ßЧ£¬Ò»Ð©ÔÚÖ÷Êý¾Ý¿âÖÐÓò»µ½µÄÊý¾ÝÐÅÏ¢£¬ÔÚReportingDatabase¿ÉÒÔ²»Óô洢¡£

3.¿ÉÒÔ¶ÔReportingDatabaseÖØ¹¹ÓÅ»¯£¬¶ø²»ÓÃÈ¥¸Ä±ä²Ù×÷Êý¾Ý¿â¡£

4.¶ÔReportingDatabaseÊý¾Ý¿âµÄ²éѯ²»»á¸ø²Ù×÷Êý¾Ý¿â´øÀ´ÈκÎѹÁ¦¡£

5.¿ÉÒÔÕë¶Ô²»Í¬µÄ²éѯÇëÇó½¨Á¢²»Í¬µÄReportingDatabase¿â¡£

µ±È»ÕâÒ²ÓÐһЩȱµã£¬±ÈÈç´Ó¿âÊý¾ÝµÄ¸üС£Èç¹ûʹÓÃSQLServer£¬±¾ÉíÒ²ÌṩÁËһЩÈç¹ÊÕÏ×ªÒÆºÍ¸´ÖÆ»úÖÆÀ´·½±ã²¿Êð¡£

Èý ʲôʱºò¿ÉÒÔ¿¼ÂÇCQRS

CQRSģʽÓÐһЩÓŵ㣺

1.·Ö¹¤Ã÷È·£¬¿ÉÒÔ¸ºÔð²»Í¬µÄ²¿·Ö

2.½«ÒµÎñÉϵÄÃüÁîºÍ²éѯµÄÖ°Ôð·ÖÀëÄܹ»Ìá¸ßϵͳµÄÐÔÄÜ¡¢¿ÉÀ©Õ¹ÐԺͰ²È«ÐÔ¡£²¢ÇÒÔÚϵͳµÄÑÝ»¯ÖÐÄܹ»±£³Ö¸ß¶ÈµÄÁé»îÐÔ£¬Äܹ»·ÀÖ¹³öÏÖCRUDģʽÖУ¬¶Ô²éѯ»òÕßÐÞ¸ÄÖеÄijһ·½½øÐи͝£¬µ¼ÖÂÁíÒ»·½³öÏÖÎÊÌâµÄÇé¿ö¡£

3.Âß¼­ÇåÎú£¬Äܹ»¿´µ½ÏµÍ³ÖеÄÄÇЩÐÐΪ»òÕß²Ù×÷µ¼ÖÂÁËϵͳµÄ״̬±ä»¯¡£

4.¿ÉÒÔ´ÓÊý¾ÝÇý¶¯(Data-Driven) תµ½ÈÎÎñÇý¶¯(Task-Driven)ÒÔ¼°Ê¼þÇý¶¯(Event-Driven).

ÔÚϳ¡¾°ÖУ¬¿ÉÒÔ¿¼ÂÇʹÓÃCQRSģʽ£º

1.µ±ÔÚÒµÎñÂß¼­²ãÓкܶà²Ù×÷ÐèÒªÏàͬµÄʵÌå»òÕß¶ÔÏó½øÐвÙ×÷µÄʱºò¡£CQRSʹµÃÎÒÃÇ¿ÉÒÔ¶Ô¶ÁºÍд¶¨Ò岻ͬµÄʵÌåºÍ·½·¨£¬´Ó¶ø¿ÉÒÔ¼õÉÙ»òÕß±ÜÃâ¶Ôijһ·½ÃæµÄ¸ü¸ÄÔì³É³åÍ»

2.¶ÔÓÚһЩ»ùÓÚÈÎÎñµÄÓû§½»»¥ÏµÍ³£¬Í¨³£ÕâÀàϵͳ»áÒýµ¼Óû§Í¨¹ýһϵÁи´ÔӵIJ½ÖèºÍ²Ù×÷£¬Í¨³£»áÐèҪһЩ¸´ÔÓµÄÁìÓòÄ£ÐÍ£¬²¢ÇÒÕû¸öÍŶÓÒѾ­ÊìϤÁìÓòÇý¶¯Éè¼Æ¼¼Êõ¡£Ð´Ä£ÐÍÓкܶàºÍÒµÎñÂß¼­Ïà¹ØµÄÃüÁî²Ù×÷µÄ¶Ñ£¬ÊäÈëÑéÖ¤£¬ÒµÎñÂß¼­ÑéÖ¤À´±£Ö¤Êý¾ÝµÄÒ»ÖÂÐÔ¡£¶ÁÄ£ÐÍûÓÐÒµÎñÂß¼­ÒÔ¼°ÑéÖ¤¶Ñ£¬½ö½öÊÇ·µ»ØDTO¶ÔÏóΪÊÓͼģÐÍÌṩÊý¾Ý¡£¶ÁÄ£ÐÍ×îÖÕºÍдģÐÍÏàÒ»Ö¡£

3.ÊÊÓÃÓÚһЩÐèÒª¶Ô²éѯÐÔÄܺÍдÈëÐÔÄÜ·Ö¿ª½øÐÐÓÅ»¯µÄϵͳ£¬ÓÈÆäÊǶÁ/д±È·Ç³£¸ßµÄϵͳ£¬ºáÏòÀ©Õ¹ÊDZØÐëµÄ¡£±ÈÈ磬ÔںܶàϵͳÖжÁ²Ù×÷µÄÇëÇóʱԶ´óÓÚд²Ù×÷¡£ÎªÊÊÓ¦ÕâÖÖ³¡¾°£¬¿ÉÒÔ¿¼Âǽ«Ð´Ä£ÐͳéÀë³öÀ´µ¥¶ÀÀ©Õ¹£¬¶ø½«Ð´Ä£ÐÍÔËÐÐÔÚÒ»¸ö»òÕßÉÙÊý¼¸¸öʵÀýÉÏ¡£ÉÙÁ¿µÄдģÐÍʵÀýÄܹ»¼õÉٺϲ¢³åÍ»·¢ÉúµÄÇé¿ö

4.ÊÊÓÃÓÚһЩÍŶÓÖУ¬Ò»Ð©Óо­ÑéµÄ¿ª·¢Õß¿ÉÒÔ¹Ø×¢¸´ÔÓµÄÁìÓòÄ£ÐÍ£¬ÕâЩÓõ½Ð´²Ù×÷£¬¶øÁíһЩ¾­Ñé½ÏÉٵĿª·¢Õß¿ÉÒÔ¹Ø×¢Óû§½çÃæÉϵĶÁÐÍ¡£

5.¶ÔÓÚϵͳÔÚ½«À´»áËæ×Åʱ¼ä²»¶ÎÑÝ»¯£¬ÓпÉÄÜ»á°üº¬²»Í¬°æ±¾µÄÄ£ÐÍ£¬»òÕßÒµÎñ¹æÔò¾­³£±ä»¯µÄϵͳ

6.ÐèÒªºÍÆäËûϵͳÕûºÏ£¬ÌرðÊÇÐèÒªºÍʼþËÝÔ´Event Sourcing½øÐÐÕûºÏµÄϵͳ£¬ÕâÑù×ÓϵͳµÄÁÙʱÒì³£²»»áÓ°ÏìÕû¸öϵͳµÄÆäËû²¿·Ö¡£

µ«ÊÇÔÚÒÔϳ¡¾°ÖУ¬¿ÉÄܲ»ÊÊÒËʹÓÃCQRS£º

1.ÁìÓòÄ£ÐÍ»òÕßÒµÎñÂß¼­±È½Ï¼òµ¥£¬ÕâÖÖÇé¿öÏÂʹÓÃCQRS»á°Ñϵͳ¸ã¸´ÔÓ¡£

2.¶ÔÓÚ¼òµ¥µÄ£¬CRUDģʽµÄÓû§½çÃæÒÔ¼°ÓëÖ®Ïà¹ØµÄÊý¾Ý·ÃÎʲÙ×÷ÒѾ­×ã¹»µÄ»°£¬Ã»±ØÒªÊ¹ÓÃCQRS£¬ÕâЩ¶¼ÊÇÒ»¸ö¼òµ¥µÄ¶ÔÊý¾Ý½øÐÐÔöɾ¸Ä²é¡£

3.²»ÊʺÏÔÚÕû¸öϵͳÖе½´¦Ê¹ÓøÃģʽ¡£ÔÚÕû¸öÊý¾Ý¹ÜÀí³¡¾°ÖеÄÌØ¶¨Ä£¿éÖÐCQRS¿ÉÄܱȽÏÓÐÓᣵ«ÊÇÔÚÓÐЩµØ·½Ê¹ÓÃCQRS»áÔö¼Óϵͳ²»±ØÒªµÄ¸´ÔÓÐÔ¡£

ËÄ CQRSÓëEvent SourcingµÄ¹ØÏµ

ÔÚCQRSÖУ¬²éѯ·½Ã棬ֱ½Óͨ¹ý·½·¨²éѯÊý¾Ý¿â£¬È»ºóͨ¹ýDTO½«Êý¾Ý·µ»Ø¡£ÔÚ²Ù×÷(Command)·½Ã棬ÊÇͨ¹ý·¢ËÍCommandʵÏÖ£¬ÓÉCommandBus´¦ÀíÌØ¶¨µÄCommand£¬È»ºóÓÉCommand½«Ìض¨µÄEvent·¢²¼µ½EventBusÉÏ£¬È»ºóEventBusʹÓÃÌØ¶¨µÄHandlerÀ´´¦Àíʼþ£¬Ö´ÐÐһЩÖîÈ磬Ð޸ģ¬É¾³ý£¬¸üеȲÙ×÷¡£ÕâÀËùÓÐÓëCommandÏà¹ØµÄ²Ù×÷¶¼Í¨¹ýEventʵÏÖ¡£ÕâÑùÎÒÃÇ¿ÉÒÔͨ¹ý¼Ç¼EventÀ´¼Ç¼ϵͳµÄÔËÐÐÀúÊ·¼Ç¼£¬²¢ÇÒÄܹ»·½±ãµÄ»Ø¹öµ½Ä³Ò»Àúʷ״̬¡£Event Sourcing¾ÍÊÇÓÃÀ´½øÐд洢ºÍ¹ÜÀíʼþµÄ¡£ÕâÀï²»Õ¹¿ª½éÉÜ¡£

Îå CQRSµÄ¼òµ¥ÊµÏÖ

CQRSģʽÔÚ˼ÏëÉϱȽϼòµ¥£¬µ«ÊÇʵÏÖÉÏ»¹ÊÇÓÐЩ¸´ÔÓ¡£ËüÉæ¼°µ½DDD£¬ÒÔ¼°Event Sourcing£¬ÕâÀïʹÓÃcodeprojectÉ쵀 Introduction to CQRS ÕâÆªÎÄÕµÄÀý×ÓÀ´ËµÃ÷CQRSģʽ¡£Õâ¸öÀý×ÓÊÇÒ»¸ö¼òµ¥µÄÔÚÏß¼ÇÈÕÖ¾(Diary)ϵͳ£¬ÊµÏÖÁËÈÕÖ¾µÄÔöɾ¸Ä²é¹¦ÄÜ¡£ÕûÌå½á¹¹ÈçÏ£º

ÉÏͼºÜÇåÎúµÄ˵Ã÷ÁËCQRSÔÚ¶Áд·½ÃæµÄ·ÖÀ룬ÔÚ¶Á·½Ã棬ͨ¹ýQueryFacadeµ½Êý¾Ý¿âÀïÈ¥¶ÁÈ¡Êý¾Ý£¬Õâ¸ö¿âÓпÉÄÜÊÇReportingDB¡£ÔÚд·½Ã棬±È½Ï¸´ÔÓ£¬²Ù×÷ͨ¹ýCommand·¢Ë͵½CommandBusÉÏ£¬È»ºóÌØ¶¨µÄCommandHandler´¦ÀíÇëÇ󣬲úÉú¶ÔÓ¦µÄEvent£¬½«Eevnt³Ö¾Ã»¯ºó£¬Í¨¹ýEventBusÌØ¶¨µÄEevntHandler¶ÔÊý¾Ý¿â½øÐÐÐ޸ĵȲÙ×÷¡£

Àý×Ó´úÂë¿ÉÒÔµ½codeprojectÉÏÏÂÔØ£¬ÕûÌå½á¹¹ÈçÏ£º

ÓÉÈý¸öÏîÄ¿¹¹³É£¬Diary.CQRS°üº¬ÁËËùÓеÄDomainºÍÏûÏ¢¶ÔÏó¡£Configurationͨ¹ýʹÓÃÒ»¸öÃûΪStructMapµÄIOCÀ´³õʼ»¯Ò»Ð©±äÁ¿·½±ãWebµ÷Óã¬WebÊÇÒ»¸ö¼òµ¥µÄMVC3ÏîÄ¿£¬ÔÚControllerÖÐÓÐÓëCQRS½»»¥µÄ´úÂë¡£

ÏÂÃæ·Ö±ð¿´QueryºÍCommand·½ÃæµÄʵÏÖ£º

Query·½ÏòµÄʵÏÖ

²éѯ·½ÃæºÜ¼òµ¥£¬ÈÕÖ¾ÁбíºÍÃ÷ϸ»ñÈ¡¾ÍÊǼòµ¥µÄ²éѯ¡£ÏÂÃæÏÈ¿´Áбí²éѯ²¿·ÖµÄ´úÂë¡£

public ActionResult Index()
{
ViewBag.Model = ServiceLocator.ReportDatabase.GetItems();
return View();
}

public ActionResult Edit(Guid id)
{
var item = ServiceLocator.ReportDatabase.GetById(id);
var model = new DiaryItemDto()
{
Description = item.Description,
From = item.From,
Id = item.Id,
Title = item.Title,
To = item.To,
Version = item.Version
};
return View(model);
}

ReportDatabaseµÄGetItemsºÍGetById(id)·½·¨¾ÍÊǼòµ¥µÄ²éѯ£¬´ÓÃüÃû¿ÉÒÔ¿´³öËûÊÇReportDatabase¡£

public class ReportDatabase : IReportDatabase
{
static List<DiaryItemDto> items = new List<DiaryItemDto>();

public DiaryItemDto GetById(Guid id)
{
return items.Where(a => a.Id == id).FirstOrDefault();
}

public void Add(DiaryItemDto item)
{
items.Add(item);
}

public void Delete(Guid id)
{
items.RemoveAll(i => i.Id == id);
}

public List<DiaryItemDto> GetItems()
{
return items;
}
}

ReportDataBaseÖ»ÊÇÔÚÄÚ²¿Î¬»¤ÁËÒ»¸öListµÄDiaryItemDtoÁÐ±í¡£ÔÚʹÓõÄʱºò£¬ÊÇͨ¹ýIRepositoryDatabase¶ÔÆä½øÐвÙ×÷µÄ£¬ÕâÑù±ãÓÚmock´úÂë¡£

Query·½ÃæµÄ´úÂëºÜ¼òµ¥¡£ÔÚʵ¼ÊµÄÓ¦ÓÃÖУ¬ÕâÒ»¿é¾ÍÊÇÖ±½Ó¶ÔDB½øÐвéѯ£¬È»ºóͨ¹ýDTO¶ÔÏ󷵻أ¬Õâ¸öDB¿ÉÄÜÊÇÓ¦¶ÔÌØ¶¨³¡¾°µÄ±¨±íÊý¾Ý¿â£¬ÕâÑù¿ÉÒÔÌáÉý²éѯÐÔÄÜ¡£

ÏÂÃæÀ´¿´Command·½ÏòµÄʵÏÖ£º

Command·½ÏòµÄʵÏÖ

CommandµÄʵÏֱȽϸ´ÔÓ£¬ÏÂÃæÒÔ¼òµ¥µÄ´´½¨Ò»¸öеÄÈÕÖ¾À´ËµÃ÷¡£

ÔÚMVCµÄControlÖУ¬¿ÉÒÔ¿´µ½AddµÄControllerÖÐÖ»µ÷ÓÃÁËÒ»¾ä»°:

[HttpPost]
public ActionResult Add(DiaryItemDto item)
{
ServiceLocator.CommandBus.Send(new CreateItemCommand(Guid.NewGuid(), item.Title, item.Description, -1, item.From, item.To));

return RedirectToAction("Index");
}

Ê×ÏÈÉùÃ÷ÁËÒ»¸öCreateItemCommand£¬Õâ¸öCommandÖ»ÊDZ£´æÁËһЩ±ØÒªµÄÐÅÏ¢¡£

public class CreateItemCommand:Command
{
public string Title { get; internal set; }
public string Description { get;internal set; }
public DateTime From { get; internal set; }
public DateTime To { get; internal set; }

public CreateItemCommand(Guid aggregateId, string title,
string description,int version,DateTime from, DateTime to)
: base(aggregateId,version)
{
Title = title;
Description = description;
From = from;
To = to;
}
}

È»ºó½«Command·¢Ë͵½ÁËCommandBusÉÏ£¬Æäʵ¾ÍÊÇÈÃCommandBusÀ´Ñ¡ÔñºÏÊʵÄCommandHandlerÀ´´¦Àí¡£

public class CommandBus:ICommandBus

public class CommandBus:ICommandBus
{
private readonly ICommandHandlerFactory _commandHandlerFactory;

public CommandBus(ICommandHandlerFactory commandHandlerFactory)
{
_commandHandlerFactory = commandHandlerFactory;
}

public void Send<T>(T command) where T : Command
{
var handler = _commandHandlerFactory.GetHandler<T>();
if (handler != null)
{
handler.Execute(command);
}
else
{
throw new UnregisteredDomainCommandException("no handler registered");
}
}
}

Õâ¸öÀïÃæÐèÒªÖµµÃ×¢ÒâµÄÊÇCommandHandlerFactoryÕâ¸öÀàÐ͵ÄGetHandler·½·¨£¬Ëû½ÓÊÜÒ»¸öÀàÐÍΪTµÄ·ºÐÍ£¬ÕâÀï¾ÍÊÇÎÒÃÇ֮ǰ´«ÈëµÄCreateItemCommand¡£À´¿´ËûµÄGetHandler·½·¨¡£

public class StructureMapCommandHandlerFactory : ICommandHandlerFactory
{
public ICommandHandler<T> GetHandler<T>() where T : Command
{
var handlers = GetHandlerTypes<T>().ToList();

var cmdHandler = handlers.Select(handler =>
(ICommandHandler<T>)ObjectFactory.GetInstance(handler)).FirstOrDefault();

return cmdHandler;
}

private IEnumerable<Type> GetHandlerTypes<T>() where T : Command
{
var handlers = typeof(ICommandHandler<>).Assembly.GetExportedTypes()
.Where(x => x.GetInterfaces()
.Any(a => a.IsGenericType && a.GetGenericTypeDefinition() == typeof(ICommandHandler<>) ))
.Where(h=>h.GetInterfaces()
.Any(ii=>ii.GetGenericArguments()
.Any(aa=>aa==typeof(T)))).ToList();


return handlers;
}

}

ÕâÀï¿ÉÒÔ¿´µ½£¬ËûÊ×ÏȲéÕÒµ±Ç°µÄ³ÌÐò¼¯ÖÐ(ICommandHandler)ËùÔڵijÌÐò¼¯ÖеÄËùÓеÄʵÏÖÁËICommandHandlerµÄ½Ó¿ÚµÄÀàÐÍ£¬È»ºóÔÚËùÓеÄÀàÐÍÕÒ²éÕÒʵÏÖÁ˸÷ºÐͽӿڲ¢ÇÒ·ºÐ͵ÄÀàÐͲÎÊýÀàÐÍΪTÀàÐ͵ÄËùÓÐÀàÐÍ¡£ÒÔÉÏÃæµÄ´úÂëΪÀý£¬¾ÍÊÇÒªÕÒ³öʵÏÖÁËICommandHandler<CreateItemCommand>½Ó¿ÚµÄÀàÐÍ¡£¿ÉÒÔ¿´µ½¾ÍÊÇCreateItemCommandHandlerÀàÐÍ¡£

public class CreateItemCommandHandler : ICommandHandler<CreateItemCommand>
{
private IRepository<DiaryItem> _repository;

public CreateItemCommandHandler(IRepository<DiaryItem> repository)
{
_repository = repository;
}

public void Execute(CreateItemCommand command)
{
if (command == null)
{
throw new ArgumentNullException("command");
}
if (_repository == null)
{
throw new InvalidOperationException("Repository is not initialized.");
}
var aggregate = new DiaryItem(command.Id, command.Title, command.Description, command.From, command.To);
aggregate.Version = -1;
_repository.Save(aggregate, aggregate.Version);
}
}

ÕÒµ½Ö®ºóÈ»ºóʹÓÃIOCʵÀý»¯Á˸öÔÏ󷵻ء£

ÏÖÔÚCommandBusÖУ¬ÕÒµ½ÁË´¦ÀíÌØ¶¨CommandµÄHandler¡£È»ºóÖ´ÐиÃÀàÐ͵ÄExecute·½·¨¡£

¿ÉÒÔ¿´µ½ÔÚ¸ÃÀàÐÍÖÐʵÀý»¯ÁËÒ»¸öÃûΪaggregateµÄDiaryItem¶ÔÏó¡£Õâ¸öºÍÎÒÃÇ֮ǰ²éѯËùÓõ½µÄDiaryItemDtoÓÐËù²»Í¬£¬Õâ¸öÒ»¸öÁìÓò¶ÔÏó£¬ÀïÃæ°üº¬ÁËһϵÁÐʼþ¡£

public class DiaryItem : AggregateRoot, 
IHandle<ItemCreatedEvent>,
IHandle<ItemRenamedEvent>,
IHandle<ItemFromChangedEvent>,
IHandle<ItemToChangedEvent>,
IHandle<ItemDescriptionChangedEvent>,
IOriginator
{
public string Title { get; set; }

public DateTime From { get; set; }
public DateTime To { get; set; }
public string Description { get; set; }

public DiaryItem()
{

}

public DiaryItem(Guid id,string title, string description, DateTime from, DateTime to)
{
ApplyChange(new ItemCreatedEvent(id, title,description, from, to));
}

public void ChangeTitle(string title)
{
ApplyChange(new ItemRenamedEvent(Id, title));
}

public void Handle(ItemCreatedEvent e)
{
Title = e.Title;
From = e.From;
To = e.To;
Id = e.AggregateId;
Description = e.Description;
Version = e.Version;
}

public void Handle(ItemRenamedEvent e)
{
Title = e.Title;
}
...
}

ItemCreatedEvent ʼþµÄ¶¨ÒåÈçÏ£¬Æäʵ¾ÍÊÇÓÃÀ´´æ´¢´«Êä¹ý³ÌÖÐÐèÒªÓõ½µÄÊý¾Ý¡£

public class ItemCreatedEvent:Event
{
public string Title { get; internal set; }
public DateTime From { get; internal set; }
public DateTime To { get; internal set; }
public string Description { get;internal set; }

public ItemCreatedEvent(Guid aggregateId, string title ,
string description, DateTime from, DateTime to)
{
AggregateId = aggregateId;
Title = title;
From = from;
To = to;
Description = description;
}
}

¿ÉÒÔ¿´µ½ÔÚDomain¶ÔÏóÖУ¬³ýÁ˶¨Òå»ù±¾µÄ×Ö¶ÎÍ⣬»¹¶¨ÒåÁËһЩÏàÓ¦µÄʼþ£¬±ÈÈçÔÚ¹¹Ô캯ÊýÖУ¬Êµ¼ÊÉÏÊÇ·¢ÆðÁËÒ»¸öÃûΪItemCreateEventµÄʼþ£¬Í¬Ê±»¹¶¨ÒåÁË´¦Àíʱ¼äµÄÂß¼­£¬ÕâЩÂß¼­¶¼·ÅÔÚÃûΪHandleµÄ½Ó¿Ú·½·¨·¢£¬ÀýÈçItemCerateEventµÄ´¦Àí·½·¨ÎªHandle(ItemCreateEvent)·½·¨¡£

ApplyChange·½·¨ÔÚAggregateRoot¶ÔÏóÖУ¬ËûÊǾۼ¯¸ù£¬ÕâÊÇDDDÖеĸÅÄͨ¹ýÕâ¸ö¸ù¿ÉÒÔ´®ÆðËùÓжÔÏó¡£ ¸ÃÀàʵÏÖÁËIEventProvider½Ó¿Ú£¬Ëû±£´æÁËËùÓÐÔÚ_changesÖеÄËùÓÐûÓÐÌá½»µÄ±ä¸ü£¬ÆäÖеÄApplyChangeµÄÓÃÀ´ÎªÌض¨µÄEvent²éÕÒEventhandlerµÄ·½·¨£º

public abstract class AggregateRoot : IEventProvider
{
private readonly List<Event> _changes;

public Guid Id { get; internal set; }
public int Version { get; internal set; }
public int EventVersion { get; protected set; }

protected AggregateRoot()
{
_changes = new List<Event>();
}

public IEnumerable<Event> GetUncommittedChanges()
{
return _changes;
}

public void MarkChangesAsCommitted()
{
_changes.Clear();
}

public void LoadsFromHistory(IEnumerable<Event> history)
{
foreach (var e in history) ApplyChange(e, false);
Version = history.Last().Version;
EventVersion = Version;
}

protected void ApplyChange(Event @event)
{
ApplyChange(@event, true);
}

private void ApplyChange(Event @event, bool isNew)
{
dynamic d = this;

d.Handle(Converter.ChangeTo(@event, @event.GetType()));
if (isNew)
{
_changes.Add(@event);
}
}
}

ÔÚApplyChangeµÄʵÏÖÖУ¬thisÆäʵ¾ÍÊǶÔÓ¦µÄʵÏÖÁËAggregateRootµÄDiaryItemµÄDomain¶ÔÏ󣬵÷ÓõÄHandle·½·¨¾ÍÊÇÎÒÃÇ֮ǰÔÚDiaryItemÖж¨ÒåµÄÐÐΪ¡£È»ºó½«¸Ãevent±£´æÔÚÄÚ²¿µÄδÌá½»µÄʼþÁбíÖС£Ïà¹ØµÄÐÅÏ¢¼°Ê¼þ¶¼±£´æÔÚÁ˶¨ÒåµÄaggregate¶ÔÏóÖв¢·µ»Ø¡£

È»ºóCommand¼ÌÐøÖ´ÐУ¬È»ºóµ÷ÓÃÁË_repository.Save(aggregate, aggregate.Version);Õâ¸ö·½·¨¡£ÏÈ¿´Õâ¸öRepository¶ÔÏó¡£

public class Repository<T> : IRepository<T> where T : AggregateRoot, new()
{
private readonly IEventStorage _storage;
private static object _lockStorage = new object();

public Repository(IEventStorage storage)
{
_storage = storage;
}

public void Save(AggregateRoot aggregate, int expectedVersion)
{
if (aggregate.GetUncommittedChanges().Any())
{
lock (_lockStorage)
{
var item = new T();

if (expectedVersion != -1)
{
item = GetById(aggregate.Id);
if (item.Version != expectedVersion)
{
throw new ConcurrencyException(string.Format("Aggregate {0} has been previously modified",
item.Id));
}
}

_storage.Save(aggregate);
}
}
}

public T GetById(Guid id)
{
IEnumerable<Event> events;
var memento = _storage.GetMemento<BaseMemento>(id);
if (memento != null)
{
events = _storage.GetEvents(id).Where(e=>e.Version>=memento.Version);
}
else
{
events = _storage.GetEvents(id);
}
var obj = new T();
if(memento!=null)
((IOriginator)obj).SetMemento(memento);

obj.LoadsFromHistory(events);
return obj;
}
}

Õâ¸ö·½·¨Ö÷ÒªÊÇÓÃÀ´¶Ôʼþ½øÐг־û¯µÄ¡£ ËùÓеľۺϵı䶯¶¼»á´æÔÚ¸ÃRepositoryÖУ¬Ê×ÏÈ£¬¼ì²éµ±Ç°µÄ¾ÛºÏÊÇ·ñºÍ֮ǰ´æ´¢ÔÚstorageÖеľۺÏÒ»Ö£¬Èç¹û²»Ò»Ö£¬Ôò±íʾ¶ÔÏóÔÚÆäËûµØ·½±»¸ü¸Ä¹ý£¬Å׳öConcurrencyException£¬·ñÔò½«¸Ã±ä¶¯±£´æÔÚEvent StorageÖС£

IEventStorageÓÃÀ´´æ´¢ËùÓеÄʼþ£¬ÆäʵÏÖÀàÐÍΪInMemoryEventStorage¡£

public class InMemoryEventStorage:IEventStorage
{
private List<Event> _events;
private List<BaseMemento> _mementos;

private readonly IEventBus _eventBus;

public InMemoryEventStorage(IEventBus eventBus)
{
_events = new List<Event>();
_mementos = new List<BaseMemento>();
_eventBus = eventBus;
}

public IEnumerable<Event> GetEvents(Guid aggregateId)
{
var events = _events.Where(p => p.AggregateId == aggregateId).Select(p => p);
if (events.Count() == 0)
{
throw new AggregateNotFoundException(string.Format("Aggregate with Id: {0} was not found", aggregateId));
}
return events;
}

public void Save(AggregateRoot aggregate)
{
var uncommittedChanges = aggregate.GetUncommittedChanges();
var version = aggregate.Version;

foreach (var @event in uncommittedChanges)
{
version++;
if (version > 2)
{
if (version % 3 == 0)
{
var originator = (IOriginator)aggregate;
var memento = originator.GetMemento();
memento.Version = version;
SaveMemento(memento);
}
}
@event.Version=version;
_events.Add(@event);
}
foreach (var @event in uncommittedChanges)
{
var desEvent = Converter.ChangeTo(@event, @event.GetType());
_eventBus.Publish(desEvent);
}
}

public T GetMemento<T>(Guid aggregateId) where T : BaseMemento
{
var memento = _mementos.Where(m => m.Id == aggregateId).Select(m=>m).LastOrDefault();
if (memento != null)
return (T) memento;
return null;
}

public void SaveMemento(BaseMemento memento)
{
_mementos.Add(memento);
}
}

ÔÚGetEvent·½·¨ÖУ¬»áÕÒµ½ËùÓеľۺϸùIdÏà¹ØµÄʼþ¡£ÔÚSave·½·¨ÖУ¬½«ËùÓеÄʼþ±£´æÔÚÄÚ´æÖУ¬È»ºóÿ¸ôÈý¸öʼþ½¨Á¢Ò»¸ö¿ìÕÕ¡£¿ÉÒÔ¿´µ½ÕâÀïÃæÊ¹ÓÃÁ˱¸Íü¼ģʽ¡£

È»ºóÔÚforeachÑ­»·ÖУ¬¶ÔÓÚËùÓеÄûÓÐÌá½»µÄ±ä¸ü£¬EventBus½«¸Ãʼþ·¢²¼³öÈ¥¡£

ÏÖÔÚ£¬ËùÓеķ¢Éú±ä¸üµÄʼþÒѾ­¼Ç¼ÏÂÀ´ÁË¡£Ê¼þÒѾ­±»·¢²¼µ½EventBusÉÏ£¬È»ºó¶ÔÓ¦µÄEventHandlerÔÙ´¦Àí¶ÔÓ¦µÄʼþ£¬È»ºóÓëDB½»»¥¡£ÏÖÔÚÀ´¿´EventBusµÄPublish·½·¨¡£

public class EventBus:IEventBus
{
private IEventHandlerFactory _eventHandlerFactory;

public EventBus(IEventHandlerFactory eventHandlerFactory)
{
_eventHandlerFactory = eventHandlerFactory;
}

public void Publish<T>(T @event) where T : Event
{
var handlers = _eventHandlerFactory.GetHandlers<T>();
foreach (var eventHandler in handlers)
{
eventHandler.Handle(@event);
}
}
}

¿ÉÒÔ¿´µ½EventBusµÄPublishºÍCommandBusÖеÄSend·½·¨ºÜÏàËÆ£¬¶¼ÊÇÊ×ÏÈͨ¹ýEventHandlerFactory²éÕÒ¶ÔÓ¦EventµÄHandler£¬È»ºóµ÷ÓÃÆäHandler·½·¨¡£±ÈÈç

public class StructureMapEventHandlerFactory : IEventHandlerFactory
{
public IEnumerable<IEventHandler<T>> GetHandlers<T>() where T : Event
{
var handlers = GetHandlerType<T>();

var lstHandlers = handlers.Select(handler => (IEventHandler<T>) ObjectFactory.GetInstance(handler)).ToList();
return lstHandlers;
}

private static IEnumerable<Type> GetHandlerType<T>() where T : Event
{

var handlers = typeof(IEventHandler<>).Assembly.GetExportedTypes()
.Where(x => x.GetInterfaces()
.Any(a => a.IsGenericType && a.GetGenericTypeDefinition() == typeof(IEventHandler<>)))
.Where(h => h.GetInterfaces()
.Any(ii => ii.GetGenericArguments()
.Any(aa => aa == typeof(T))))
.ToList();
return handlers;
}
}

È»ºó·µ»Ø²¢ÊµÀý»¯ÁËItemCreatedEventHandler ¶ÔÏ󣬸öÔÏóµÄʵÏÖÈçÏ£º

public class ItemCreatedEventHandler : IEventHandler<ItemCreatedEvent>
{
private readonly IReportDatabase _reportDatabase;
public ItemCreatedEventHandler(IReportDatabase reportDatabase)
{
_reportDatabase = reportDatabase;
}
public void Handle(ItemCreatedEvent handle)
{
DiaryItemDto item = new DiaryItemDto()
{
Id = handle.AggregateId,
Description = handle.Description,
From = handle.From,
Title = handle.Title,
To=handle.To,
Version = handle.Version
};

_reportDatabase.Add(item);
}
}

¿ÉÒÔ¿´µ½ÔÚHandler·½·¨ÖУ¬´ÓʼþÖлñÈ¡²ÎÊý£¬È»ºóн¨DTO¶ÔÏó£¬È»ºó½«¸Ã¶ÔÏó¸üе½DBÖС£

µ½´Ë£¬Õû¸öCommandÖ´ÐÐÍê³É¡£

Áù ½áÓï

CQRSÊÇÒ»ÖÖ˼ÏëºÜ¼òµ¥ÇåÎúµÄÉè¼ÆÄ£Ê½£¬Ëûͨ¹ýÔÚÒµÎñÉÏ·ÖÀë²Ù×÷ºÍ²éѯÀ´Ê¹µÃϵͳ¾ßÓиüºÃµÄ¿ÉÀ©Õ¹ÐÔ¼°ÐÔÄÜ£¬Ê¹µÃÄܹ»¶ÔϵͳµÄ²»Í¬²¿·Ö½øÐÐÀ©Õ¹ºÍÓÅ»¯¡£ÔÚCQRSÖУ¬ËùÓеÄÉæ¼°µ½¶ÔDBµÄ²Ù×÷¶¼ÊÇͨ¹ý·¢ËÍCommand£¬È»ºóÌØ¶¨µÄCommand´¥·¢¶ÔӦʼþÀ´Íê³É²Ù×÷£¬Õâ¸ö¹ý³ÌÊÇÒì²½µÄ£¬²¢ÇÒËùÓÐÉæ¼°µ½¶ÔϵͳµÄ±ä¸üÐÐΪ¶¼°üº¬ÔÚ¾ßÌåµÄʼþÖУ¬½áºÏEventing Sourceģʽ£¬¿ÉÒԼǼÏÂËùÓеÄʼþ£¬¶ø²»ÊÇÒÔÍùµÄijһµãµÄÊý¾ÝÐÅÏ¢£¬ÕâЩÐÅÏ¢¿ÉÒÔ×÷ΪϵͳµÄ²Ù×÷ÈÕÖ¾£¬¿ÉÒÔÀ´¶Ôϵͳ½øÐлØÍË»òÕßÖØ·Å¡£

CQRS ģʽÔÚʵÏÖÉÏÓÐЩ¸´ÔÓ£¬ºÜ¶àµØ·½±ÈÈçAggregationRoot¡¢Domain Object¶¼Éæ¼°µ½DDDÖеÄÏà¹Ø¸ÅÄ±¾È˶ÔDDD²»Ì«¶®¡£ÕâÀï½öΪÁËÑÝʾCQRSģʽ£¬ËùÒÔʹÓõÄÀý×ÓÊÇcodeprojectÉϵģ¬Ä©Î²ÁгöÁËһЩ²Î¿¼ÎÄÕ£¬Èç¹ûÄúÏëÁ˽â¸ü¶à£¬¿ÉÒÔÓÐÕë¶ÔÐÔµÄÔĶÁ¡£

×îºó£¬Ï£ÍûCQRSģʽÄÜÈÃÄúÔÚÉè¼Æ¸ßÐÔÄÜ£¬¿ÉÀ©Õ¹ÐԵijÌÐòʱÄܹ»¶àÒ»ÖÖÑ¡ÔñºÍ¿¼ÂÇ¡£

   
3519 ´Îä¯ÀÀ       27
Ïà¹ØÎÄÕÂ

ÆóÒµ¼Ü¹¹¡¢TOGAFÓëArchiMate¸ÅÀÀ
¼Ü¹¹Ê¦Ö®Â·-ÈçºÎ×öºÃÒµÎñ½¨Ä££¿
´óÐÍÍøÕ¾µçÉÌÍøÕ¾¼Ü¹¹°¸ÀýºÍ¼¼Êõ¼Ü¹¹µÄʾÀý
ÍêÕûµÄArchimateÊÓµãÖ¸ÄÏ£¨°üÀ¨Ê¾Àý£©
Ïà¹ØÎĵµ

Êý¾ÝÖÐ̨¼¼Êõ¼Ü¹¹·½·¨ÂÛÓëʵ¼ù
ÊÊÓÃArchiMate¡¢EA ºÍ iSpace½øÐÐÆóÒµ¼Ü¹¹½¨Ä£
ZachmanÆóÒµ¼Ü¹¹¿ò¼Ü¼ò½é
ÆóÒµ¼Ü¹¹ÈÃSOAÂ䵨
Ïà¹Ø¿Î³Ì

ÔÆÆ½Ì¨Óë΢·þÎñ¼Ü¹¹Éè¼Æ
ÖÐ̨սÂÔ¡¢ÖÐ̨½¨ÉèÓëÊý×ÖÉÌÒµ
ÒÚ¼¶Óû§¸ß²¢·¢¡¢¸ß¿ÉÓÃϵͳ¼Ü¹¹
¸ß¿ÉÓ÷ֲ¼Ê½¼Ü¹¹Éè¼ÆÓëʵ¼ù
×îл¼Æ»®
DeepSeekÔÚÈí¼þ²âÊÔÓ¦ÓÃʵ¼ù 4-12[ÔÚÏß]
DeepSeek´óÄ£ÐÍÓ¦Óÿª·¢Êµ¼ù 4-19[ÔÚÏß]
UAF¼Ü¹¹ÌåϵÓëʵ¼ù 4-11[±±¾©]
AIÖÇÄÜ»¯Èí¼þ²âÊÔ·½·¨Óëʵ¼ù 5-23[ÉϺ£]
»ùÓÚ UML ºÍEA½øÐзÖÎöÉè¼Æ 4-26[±±¾©]
ÒµÎñ¼Ü¹¹Éè¼ÆÓ뽨ģ 4-18[±±¾©]

ר¼ÒÊӽǿ´ITÓë¼Ü¹¹
Èí¼þ¼Ü¹¹Éè¼Æ
ÃæÏò·þÎñÌåϵ¼Ü¹¹ºÍÒµÎñ×é¼þ
ÈËÈËÍøÒÆ¶¯¿ª·¢¼Ü¹¹
¼Ü¹¹¸¯»¯Ö®ÃÕ
̸ƽ̨¼´·þÎñPaaS

ÃæÏòÓ¦Óõļܹ¹Éè¼ÆÊµ¼ù
µ¥Ôª²âÊÔ+ÖØ¹¹+Éè¼ÆÄ£Ê½
Èí¼þ¼Ü¹¹Ê¦¡ª¸ß¼¶Êµ¼ù
Èí¼þ¼Ü¹¹Éè¼Æ·½·¨¡¢°¸ÀýÓëʵ¼ù
ǶÈëʽÈí¼þ¼Ü¹¹Éè¼Æ¡ª¸ß¼¶Êµ¼ù
SOAÌåϵ½á¹¹Êµ¼ù

Èñ°²¿Æ¼¼ Èí¼þ¼Ü¹¹Éè¼Æ·½·¨
³É¶¼ ǶÈëʽÈí¼þ¼Ü¹¹Éè¼Æ
ÉϺ£Æû³µ ǶÈëʽÈí¼þ¼Ü¹¹Éè¼Æ
±±¾© Èí¼þ¼Ü¹¹Éè¼Æ
ÉϺ£ Èí¼þ¼Ü¹¹Éè¼Æ°¸ÀýÓëʵ¼ù
±±¾© ¼Ü¹¹Éè¼Æ·½·¨°¸ÀýÓëʵ¼ù
ÉîÛÚ ¼Ü¹¹Éè¼Æ·½·¨°¸ÀýÓëʵ¼ù
ǶÈëʽÈí¼þ¼Ü¹¹Éè¼Æ¡ª¸ß¼¶Êµ¼ù