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