ãã¯ããã°ãã«ã¡ã¯ããã¨ãã ãã§ãã ä¹ ãã¶ãã«ããã°ãæ¸ãâ¦ãæè¿ã趣å³ã§Angular2ããReactãããã£ã¦ãã¾ãããã£ã¨Webpackã«ãªãã¾ããâ¦ã
ãã¦ãä»åã®ãé¡ã¯ãFluxã¨DDDã®çµ±åæ¹æ³ãã«ã¤ãã¦ãAngular2ãå ã«è§¦ã£ã¦ãã¾ããããFluxã¨ããã°ãã¯ãReactã ããã¨ãããã¨ã§éä¸ã§æµ®æ°ãã¦Reactã§èãã¦ãã¾ããAngular2ã§ãã§ããã¯ãã§ãããä»åã¯Reactã§çµ±åæ¹æ³*1ã«ã¤ãã¦èãã¦ã¿ããã¨æãã¾ããä¸ã¤æã£ã¦ããã¨ãFluxã¯DDDã¨çµ±åãããã¨ãæ³å®ãã¦ããªãè¨è¨ãã¿ã¼ã³ãªãã§äºã ã¨ãã¯ããã§ã¯èãã¦ãã¾ãããããã¯ãã®ããã°è¨äºãèªãèªã¾ãªãã«é¢ããããèªèº«ã§å¤æããã¦ãã ãããã½ã¼ã¹ã³ã¼ãã«ã¤ãã¦ã¯ãGithubã¸ã®ãªã³ã¯ãä¸çªä¸ã«æ¸ãã¦ããã®ã§èå³ããã人ã¯åèã«ãã¦ã¿ã¦ãã ããã
Fluxã£ã¦ä½ï¼
ã¾ãåºç¤ã¨ãããã¨ã§ãFlux is ä½ããã
æ¬å®¶æ°ããã¯ã©ã¤ã¢ã³ããµã¤ãã¢ããªã±ã¼ã·ã§ã³ãæ§ç¯ããããã«Facebookã使ã£ã¦ããã¢ããªã±ã¼ã·ã§ã³ã¢ã¼ããã¯ãã£ãå³ãè¦ãã°ãããããã«ä¸æ¹åã®ãã¼ã¿ããã¼ãæä¾ããã®ãç¹å¾´ã§ããFacebookãéçºãã¦ããã®ã§ãReactã¨ä¸ç·ã«ä½¿ããããã¨ãå¤ãã¨æãã¾ããããã¬ã¼ã ã¯ã¼ã¯ã¨ããããè¨è¨ãã¿ã¼ã³ã®ãããªãã®ã§ãã
ã§ã¯ãFluxã®åã³ã³ãã¼ãã³ãã®åã®è§£éããã¨ãããã¨ã§ä»¥ä¸ãåç §ã
Flux | Application Architecture for Building User Interfaces
Viewã¯ãããã¨æãã®ã§ãAction, Action Creator, Dispatcher, Storeãã¿ã¦ããã¾ããããç¹ã«Storeã£ã¦ãªãããã£ã¦è©±ãå¤ãã®ã§æ´çãã¾ããFluxã«ã¤ãã¦è§£éãééã£ã¦ããã¨ãããããã°ææãã¦ããããã¨ããããã§ãã
Action with Action Creator
Action Creatorã¯ã¡ã½ããã®ãã©ã¡ã¼ã¿ããã¢ã¯ã·ã§ã³ãçæããããã«ãã¼ã¡ã½ããã®éåãActionã«ã¯ã¿ã¤ããã¢ãµã¤ã³ããããããDispatcherã«æä¾ããã
Dispatcher
ãã¹ã¦ã®Actionã¯ãStoreãDispathcerã«ç»é²ããã³ã¼ã«ããã¯ãçµç±ãã¦ãã¹ã¦ã®Storeã«éãããã
- ãã¹ã¦ã®Actionã¯StoreãDispatcherã«ç»é²ããã³ã¼ã«ããã¯ãçµç±ãã¦Storeã«éãããã
- Dispatcherã¯ãFluxã¢ããªã±ã¼ã·ã§ã³ã®ããã®ãã¼ã¿ããã¼ã管çããä¸å¤®ããçãªåå¨ã§ãã¢ããªã±ã¼ã·ã§ã³åºæã®è¦ä»¶ãå«ã¾ãªãã·ã³ãã«ãªé éã·ã¹ãã ã
- Action Creatorã¯æ°ããActionãDispatcherã«æä¾ããã
Dispatcherã¯Publisher -> (Subscriber - Publisher) -> Subscriber ã¿ãããªãã®ã§ãããæè¡çãªè¦ä»¶ããå«ã¾ãªããã
Store
ã¯ããããããããã¼ã¨è¨ããããã®ã
ã¹ãã¢ãã¢ã¯ã·ã§ã³ã¸ã®ã¬ã¹ãã³ã¹å¦çã§ã¹ãã¢èªèº«ãæ´æ°ããå¾ãå¤æ´ã¤ãã³ããéä¿¡ããã
- Storeã¯ã¢ããªã±ã¼ã·ã§ã³ç¶æ ã¨ã¢ããªã±ã¼ã·ã§ã³ãã¸ãã¯ã表ããã®ã
- MVCã®ã¢ãã«ç¸å½ã ããããããã®ãªãã¸ã§ã¯ãç¶æ ã管çãããORMã¢ãã«ãããªåä¸ã®ã¢ãã«ã表ç¾ããªãã
- TodoStoreã¯Todoã¢ã¤ãã ã®ã³ã¬ã¯ã·ã§ã³ã管çãããã®ã«ä¼¼ã¦ãããStoreã¯ã¢ãã«ã®ã³ã¬ã¯ã·ã§ã³ã¨è«çé åã®ã·ã³ã°ã«ãã³ãªã¢ãã«ã®ä¸¡æ¹ã®è¡¨ç¾ããã
- Storeã¯Dispatcherã使ã£ã¦èªåèªèº«ã®ç»é²ã¨ã³ã¼ã«ããã¯ãæä¾ãããã³ã¼ã«ããã¯ã¯ãã©ã¡ã¼ã¿ã¨ãã¦Actionãåãåãã
- Storeã«ç»é²ãããã³ã¼ã«ããã¯ã®å é¨ã«ãããã¢ã¯ã·ã§ã³ã¿ã¤ãã«åºã¥ãswitchæãActionã解éããããã«ä½¿ãããé©åãªããã¯ãæä¾ãããããã«ãã£ã¦ãDispatcherçµç±ã§Storeãæã¤ã¢ããªã±ã¼ã·ã§ã³ç¶æ ãæ´æ°ã§ããããã«ãªãã
- Storeãæ´æ°ãããå¾ã¯ãæ´æ°ã¤ãã³ããããã¼ããã£ã¹ãããããã¥ã¼ãæ°ããç¶æ ãStoreã«åãåããããããã¥ã¼èªä½ãæ´æ°ãããããããªãã
ããã§ããããã¨ã¯ãStoreã¯MVCã®ã¢ãã«ç¸å½ã¨ãããã¨ã¨ãã¢ããªã±ã¼ã·ã§ã³ç¶æ ã¨ã¢ããªã±ã¼ã·ã§ã³ãã¸ãã¯ãå«ãã¨ãããã¨ã§ãããã®ã¢ãã«ã®æ¯ãèãã¯ã³ã¼ã«ããã¯ã®ä¸ã«ããã¨ãããã¨ã«ãªãã¾ãã
ã¾ã¨ããã¨ãActionã¯ã¡ãã»ã¼ã¸ã¨ãã¦Dispatcherã«æ¸¡ãã¨ãActionã¯Dispatcherçµç±ã§Storeã«éããããStoreã¯ã¢ããªã±ã¼ã·ã§ã³ç¶æ ãæã£ã¦ãã¦ãActionã«å¿ããæ¯ãèããèµ·ããç¶æ ãå¤åããããç¶æ å¤åãèµ·ããã¨Viewã«éç¥ããããæ´æ°ã®éç¥ãåãåã£ãViewã¯Storeããç¶æ ãåãåºããããèªåèªèº«ã®ç¶æ ãæ´æ°ããå¯è½æ§ããããã¨ãããã¨ã«ãªãã¨æãã¾ãã
DDDã§ã¯ã©ãèãããï¼
第2é¨ ã¢ãã«é§åè¨è¨ã®æ§æè¦ç´ ããèãã¦ã¿ã¾ãã
ã¬ã¤ã¤ã¼åã¢ã¼ããã¯ãã£
ãã¡ã¤ã³å±¤ ãã¸ãã¹ã®æ¦å¿µã¨ããã¸ãã¹ãç½®ãããç¶æ³ã«é¢ããæ å ±ãããã³ãã¸ãã¹ã«ã¼ã«ã表ã責åãè² ãããã¸ãã¹ã®ç¶æ³ãåæ ããç¶æ ã¯ããã§å¶å¾¡ãã使ç¨ããããããããæ ¼ç´ããã¨ããæè¡çãªè©³ç´°ã¯ãã¤ã³ãã©ã¹ãã©ã¯ãã£ã«å§è²ãããããã®å±¤ããã¸ãã¹ã½ããã¦ã§ã¢ã®æ ¸å¿ã§ããã
ãã¸ãã¹ã¨ããè¨èã¯å°ã大ããã«èãããããããã¾ãããç°¡åã«è¨ãæãããªãã°ããã®ã½ããã¦ã§ã¢ã§è§£æ±ºãã¹ãé¢å¿äºã¨ãç¥èã¨ããæå³ã§ããTodoã¢ããªã±ã¼ã·ã§ã³ãªãã°ãTodoã®ã¿ã¤ãã«ãæéãæ å½è ãéè¦åº¦ãåªå 度ãªã©ãå½ã¦ã¯ã¾ãããããã¾ãããèªåãæ å½ãã¦ããTodoãä»ã©ãã ãããããæéåããã¦ããTodoã¯ä½ããªã©ã®ç¶æ ãä¿æãã¦ãã¾ã(ç¶æ ã®ä¿æã«ã¤ãã¦ã®æè¡çé½åã¯ãã®å±¤ã®è²¬åå¤)ãããããæå³ã§ã¯ãã½ããã¦ã§ã¢ã®ã³ã¢ã¯ãã¡ã¤ã³ã§ããããå°ã詳ããããã¨ãä»ã®ã¬ã¤ã¤ã¼ã®ãã¹ã¦ã®ã³ã³ãã¼ãã³ããç´æ¥çãéæ¥çãã«ããããã¡ã¤ã³å±¤ã«ä¾åãã¾ããAngularãReactãªã©ã®ãã¬ã¼ã ã¯ã¼ã¯ã«ãã£ã¦å®è£ ãããã¢ããªã±ã¼ã·ã§ã³ã§ãã£ã¦ãããã¡ã¤ã³å±¤ã«å¾ãã¾ãã
ã¢ããªã±ã¼ã·ã§ã³å±¤ ã½ããã¦ã§ã¢ãè¡ããã¨ã«ãªã£ã¦ããä»äºãå®ç¾©ãã表ç¾åè±ããªãã¡ã¤ã³ãªãã¸ã§ã¯ããåé¡ã解決ããããã«å°ãããã®ã¬ã¤ã¤ã責åãè² ãä½æ¥ã¯ããã¸ãã¹ ã«ã¨ã£ã¦æå³ããããã®ãããããã¯ä»ã·ã¹ãã ã®ã¢ããªã±ã¼ã·ã§ã³å±¤ã¨ç¸äºä½ç¨ããã®ã«å¿ è¦ãªãã®ã§ããããã®ã¬ã¤ã¤ã¯èãä¿ãããããã¸ãã¹ã«ã¼ã«ãç¥èãå«ã¾ããããã¹ãä½æ¥ã調æ´ããã ãã§ãå®éã®å¦çã¯ããã¡ã¤ã³ãªãã¸ã§ã¯ãã«ãã£ã¦ç´ä¸ã®ã¬ã¤ã¤ã§å®è¡ãããå ±åä½æ¥ã«å§è²ããããã¸ãã¹ã®ç¶æ³ãåæ ããç¶æ ã¯æããªãããã¦ã¼ã¶ãããã°ã©ã ãè¡ãä½æ¥ã®é²æãåæ ããç¶æ ãæã¤ãã¨ã¯ã§ããã
ã¢ããªã±ã¼ã·ã§ã³å±¤ããããã«ãããã®ã®ä¸ã¤ããããã¾ãããããã¡ã¤ã³ã¢ãã«ã®ç¶æ ãæ¯ãèããå調åä½ããã¦ã¢ããªã±ã¼ã·ã§ã³ä¸ã®ä¸ã¤ã®ã¦ã¼ã¹ã±ã¼ã¹ãå®ç¾ãããã®ã§ããä¾ãã°ããã£ããã®é¨å±ã§ã誰ãã«ã¡ã³ã·ã§ã³ããã¨ããã£ããã¨ãããã¡ã¤ã³ã¢ãã«ãæ´æ°ããã ãã§ã¯ãªããToå ã®ã¦ã¼ã¶ã¢ã«ã¦ã³ãã®ããã¤ã¹ã»ãã·ã§ã³ãæ¢ããéç¥ããªããã°ãªããªãããããã¾ããããããã¢ããªã±ã¼ã·ã§ã³ãã¸ãã¯ã¨è¨ããããã®ã§ãå¤ãã®å ´åã¯ã¦ã¼ã¹ã±ã¼ã¹ãæ ãã¢ããªã±ã¼ã·ã§ã³ãµã¼ãã¹ã®è²¬åã¨ãªãã¾ãã
DDDã§ã¯ã¢ããªã±ã¼ã·ã§ã³ç¶æ ã¯ãã¡ã¤ã³å±¤ãæ¡ã£ã¦ãã¾ãããããå°ã詳細ã«è¿°ã¹ãã¨ãéç´ã¨ãªãã¸ããªã¨ãããã®ãæ ã£ã¦ãã¾ããéç´ã¯å é¨ã«ãã¡ã¤ã³ã¢ãã«ãè¤æ°æ ¼ç´ããã¦ãã¦ããããã®è¤æ°ã®é¢é£ãã²ã¨ã¾ã¨ã¾ãã¨ãã¦è¡¨ç¾ãã¦ãã¾ããããã¦ãéç´ãæ°¸ç¶åããéã¯ãªãã¸ããªã«ä¾é ¼ãã¾ããä¸è¬çã«å®è£ ãããã¤ã³ã¿ã¼ãã§ã¤ã¹å½¢å¼ã¨ãã¦ã¯put(todo: Todo)ãgetByid(id: TodoId), getAll(): List[Todo] ãªã©ãããã¾ãã対å¿ããæ°¸ç¶åããã¤ã¹ãRDBçãKVSçãAPIçãªã©æ§ã ã§ãããå¤ããã¿ãã¨Mapã®ããã«è¦ããã¤ã³ã¿ã¼ãã§ã¤ã¹ãæã£ã¦ãã¾ãããããããªãã¸ããªãåå¨ããçç±ã¨ãã¦ã¯ããã¡ã¤ã³ã¢ãã«ã¯ã½ããã¦ã§ã¢ã§è§£æ±ºããé¢å¿äºããã®ã¾ã¾è¡¨ç¾ãããããæ°¸ç¶åãªã©ã®æè¡é½åã¯ãªãã¸ããªã«ç§»è²ãã¹ãã¨ããèããããã¦ãã¾ãã
Fluxã¸ã®DDDã®çµ±åæ¹æ³
ã§ã¯ãFluxã¨ã®çµ±åã«ã¤ãã¦è©±ããå ã«é²ãã¾ããFluxã®Storeã¯ãªãã¸ããªã«ä¼¼ããããªè²¬åãæã£ã¦ããã®ã¯ä¸è¨ã§è¿°ã¹ãéãã§ããã©ã¡ããã¢ããªã±ã¼ã·ã§ã³ç¶æ ãä¿æãã責åãæ ãã¾ããã¾ããFluxã®Storeã«ã¯ã¢ããªã±ã¼ã·ã§ã³ãã¸ãã¯ãå«ã¾ãã¦ãã¾ããä¸æ¹ããªãã¸ããªã«ã¯éç´ã®æ°¸ç¶å責åããããã¾ãããDDDã§ã¯ã¢ããªã±ã¼ã·ã§ã³ãã¸ãã¯ã¯ãã¡ã¤ã³ã¢ãã«ãå調åä½ãããã¢ããªã±ã¼ã·ã§ã³ãµã¼ãã¹ã®å½¹å²ã§ãããã¤ã¾ããFlux Store = ãªãã¸ã㪠+ éç´ã®å調åä½(=ã¢ããªã±ã¼ã·ã§ã³ãµã¼ãã¹)ã¨èãããã¨ãã§ãã¾ãããã®æç¹ã§Storeã¯ã¢ããªã±ã¼ã·ã§ã³å±¤ã«ããã¨èãããã¾ãããã¸ãã¹ãã¸ãã¯ä»ãã®æ°¸ç¶åã¢ããªã±ã¼ã·ã§ã³ãµã¼ãã¹ã®ãããªãã®ã¨ãã¦è§£éãããã¨ãã§ãã¾ãã
å ¨ä½å
åç´ã«çµ±åãããªãã°ãFluxã®Storeå é¨ã§ãªãã¸ããªã¨éç´ã使ã£ã¦ãã¢ããªã±ã¼ã·ã§ã³ç¶æ ã¨ã¢ããªã±ã¼ã·ã§ã³ãã¸ãã¯ãå®è£ ããã°ãFluxãDDDã®èãæ¹ããé¸è±ããã«çµ±åãããã¨ãå¯è½ã§ãã以ä¸ãçµ±åã¤ã¡ã¼ã¸ã§ããæåã«èãããã®ããå°ãå¤ãã£ã¦ãã¾ãããæ ¹æ¬ã¯å¤ãã£ã¦ãã¾ããã
UI層ã«ã¯UIã«é¢é£ããActionã¨Action Creatorã¨Viewããã¢ããªã±ã¼ã·ã§ã³å±¤ã«ã¯Dispatcherã¨Storeãããã¡ã¤ã³å±¤ã«ã¯ãã¸ãã¹ä¸ã®æ¦å¿µã表ãéç´(ãã¡ã¤ã³ã¢ãã«ãã«ãã»ã«åãããã®)ã¨ãªãã¸ããªãããã以å¤ã®æ±ç¨çãªãã®ã¯ã¤ã³ãã©ã¹ãã©ã¯ãã£å±¤ã«é ç½®ãã¾ããã¢ããªã±ã¼ã·ã§ã³ç¶æ ãæ´æ°ããããåå¾ãããããéã«ãStoreçµç±ã§ãªãã¸ããªã¨éç´ãå©ç¨ãã¾ãããã以å¤ã®è¦ä»¶(ãã¨ãã°ãã¡ã¤ã³ã¢ãã«ã¨ã¯é¢ä¿ããªãRPCå¼ã³åºããªã©)ã¯å¥ã®æ¹æ³ã§å®ç¾ãããã¨ãèãã¦ãã¾ãããé·ããªãã®ã§å¾æ¥ã«ãã¾ãã
ã§ã¯ãFluxãè¸ã¾ãã¦å層ãã©ã®ããã«å®è£ ãããææ¡ãã¾ãã
ãã¡ã¤ã³å±¤
ã¾ãæåã«éç´ãå®ç¾©ãã¾ãããã®éç´ã¯ããã¸ãã¹ä¸ã®å©å®³é¢ä¿è ã®é ã®ä¸ã«ããæ¦å¿µãªã®ã§ãå®ç¾æ¹æ³ã§ããæè¡è¦ç´ ããã§ããéãç¬ç«ãã¦ããæ¹ãããã§ãããªã®ã§ãããã§ã¯ã¤ã³ãã©ã¹ãã©ã¯ãã£(è¦ä»¶ã®å½±é¿ãåããªãæ±ç¨çæè¡åºç¤)ã¨ãªãè¨èªæ©è½ã«ããä¾åããªãããã«è¨è¨ãã¾ããããããªãããä»åã®ãã¡ã¤ã³ã¢ãã«ã¯ãã ã®ãã¼ã¿ã®å ¥ãç©ã¨åãã¦ãã¦è²§è¡çã«ãªã£ã¦ãã¾ããæ¬æ¥ã¯ãã¸ãã¹ä¸ã®è±å¯ãªç¥èãæå½±ãããã¡ã¤ã³ã¢ãã«ã¨ãªãããã«ããªããã°ããã¾ããããä»åã®è¶£æ¨ã¨ã¯å¤ããã®ã§ã容赦ãã ããâ¦ã
export class TodoAggregate { constructor(public id: string, public text: string, public createAt: Date) { } }
次ã«ãã®éç´ã®æ°¸ç¶åãæ ããªãã¸ããªãå®è£ ãã¾ãããªãã¸ããªã¯éç´ã®ã¤ã³ã¹ã¿ã³ã¹ãä¿åãããæ¤ç´¢ããããããã¨ãã§ãã¾ã*2ãã¤ã³ã¿ã¼ãã§ã¤ã¹ã ããè¦ãã¨éç´ã®ã³ã¬ã¯ã·ã§ã³ã®ããã«è¦ããå é¨ã®å®è£ ã¯Mapã§ãã£ããããã¼ã«ã«ã¹ãã¬ã¼ã¸ã§ãã£ãããAPIãµã¼ãã§ãã£ããæ§ã ãªãã®ãå®è£ ã§ãã¾ããREST APIãµã¼ãããéç´ãI/Oããããªããå é¨ã®å®è£ ãHTTPã¯ã©ã¤ã¢ã³ãã«åãæ¿ããå¿ è¦ãããã§ãããã
export class TodoRepository { private _todos: {[id: string]: TodoAggregate} = {}; constructor(aggreates: TodoAggregate[] = []) { this.storeMulti(aggreates); } store(aggregate: TodoAggregate): void { this._todos[aggregate.id] = _.cloneDeep(aggregate); } storeMulti(aggreates: TodoAggregate[]): void { aggreates.forEach((a) => this.store(a)); } resoleBy(id: string): TodoAggregate { return _.cloneDeep(this._todos[id]); } resolveAll(): TodoAggregate[] { return Object.keys(this._todos).map((id) => this.resoleBy(id)); } }
ã¢ããªã±ã¼ã·ã§ã³å±¤
Storeã®åã«Stateã«ã¤ãã¦èª¬æãã¾ããStateã«ã¯ç»é¢ã®å ¥åç¶æ 以å¤ã«ãã¥ã¼ã«åºåããããã®ç¶æ ã¨ãã¦ãå ã»ã©å®ç¾©ãããªãã¸ããªãå«ããããã«ãã¾ã*3ã
export class TodoState { constructor(public currentTodo: string, private _repository: TodoRepository) { } getRepository = (): TodoRepository => { return _.cloneDeep(this._repository); }; }
次ã¯Storeã§ããä»åã¯FluxUtilsã®ReduceStoreã§å®è£ ãã¾ããèå¿ãªé¨åã¯reduceã¡ã½ããã¨createTodoã¡ã½ããã§ãããã®ã³ã¼ããããã¢ããªã±ã¼ã·ã§ã³ç¶æ ã¯ãªãã¸ããªãæ ã£ã¦ãããã¨ããããã¾ããä»åã®Todoã¯æ¯ãèãããªãã®ã§å¾®å¦ã§ãããè¤éãªè¦ä»¶ã§ããã°ãã®ãã¡ã¤ã³ã¢ãã«ã«è¨è¨ã®æå³ãååã«è¡¨ããæ¯ãèããå®è£ ãããã¯ãã§ãã
export class TodoStore extends FluxUtils.ReduceStore<TodoState, TodoAction> { constructor(dispatcher: Flux.Dispatcher<TodoAction>) { super(dispatcher); } getInitialState(): TodoState { return new TodoState('', new TodoRepository()); } reduce(state: TodoState, action: TodoAction): TodoState { switch (action.type) { case 'CreateTodo': return this.createTodo(state, action as CreateTodo); default: throw Error('no match error'); } } private createTodo(state: TodoState, action: CreateTodo): TodoState { console.log(action); const currentTodo = state.currentTodo; const repository = new TodoRepository(state.getRepository().resolveAll()); const aggregate = new TodoAggregate(new Guid().toString(), action.text, new Date()); repository.store(aggregate); return new TodoState(currentTodo, repository); } } export const todoStore = new TodoStore(todoDispatcher);
Dispatcherãæãã¦ããâ¦ã以ä¸ã®ä¸è¡ã§ãã
export const todoDispatcher = new Flux.Dispatcher<TodoAction>();
UI層
ãããããReactã®ã³ã³ãã¼ãã³ãã®çµåã§ãããæåã«ãã¥ã¼ã¢ãã«ãä½ãã¾ãããã®ã¢ãã«ã¯ãã¥ã¼ã«é¢é£ããç¥èã表ãã¦ãã¾ããéç´ãªã©ã®ãã¡ã¤ã³ã¢ãã«ã¯ãå®éã®ãã¸ãã¹ãªã©ã®åé¡é åã®æ¦å¿µã¨å¯¾å¿ä»ãã¾ããããã¥ã¼ã§ã¯ç°ãªã表ç¾å½¢å¼ãå¿ è¦ã«ãªãå ´åãããã¾ããéç´ã®ã¢ãã«ãç»é¢ã«ãã£ã¦å½¢å¼ãå¤ãã¦åºåããå ´åãªã©ã§ãããã¥ã¼ã¢ãã«ã®çæã¯ããããããæ¹ãããã¾ãããããã§ã¯Converterã使ã£ã¦éç´ããå¤æãã¦å°åºãã¾ãã
export class TodoViewModel { constructor(public key: string, public text: string, public dateString: string) { } }
Converterã®ã³ã¼ãã¯ä»¥ä¸ããªãã¸ããªãæã¤éç´ã®éåããã¥ã¼ã®è¦ä»¶ã«åããã¦mapãã¦ããã ãã§ãã
export class TodoViewModelConverter { constructor(private _repository: TodoRepository) { } getTodoVMs = (): TodoViewModel[] => { return this._repository.resolveAll().map((a) => { return new TodoViewModel(a.id, a.text, a.createAt.toLocaleDateString() + " " + a.createAt.toLocaleTimeString()); }); }; }
æå¾ã«ãReact.Componentã§ãããã®ã³ã³ãã¼ãã³ãã¯Action Creatorã¨Store(ãªãã¸ããªå«ã)ã«ããä¾åãã¦ãã¾ããã Viewèªèº«ãç¶æ ãæã£ã¦ãã¾ãããUIä¸ã®ä½ãã®ã¤ãã³ããèµ·ããã¨Action Creatorã使ã£ã¦ActionãDispatcherã«éä¿¡ãã¾ããããã¨ãStoreã§æ¯ãèããèµ·ããç¶æ å¤åãèµ·ãããViewãStoreã«ãããããç»é²ãããã³ãã©ãå¼ã°ãã¦Viewèªèº«ã®ç¶æ ãæ¸ãæãã¾ãã
export class TodoComponent extends React.Component<{}, TodoState> { private listenerSubscription: { remove: Function }; constructor(props: {}) { super(props); this.state = new TodoState('', new TodoRepository()); } componentDidMount() { this.listenerSubscription = todoStore.addListener(this.handleStateChange.bind(this)); } componentWillUnmount() { this.listenerSubscription.remove(); } handleStateChange() { const storeState = todoStore.getState(); const newRepository = new TodoRepository(storeState.getRepository().resolveAll()); const newState = new TodoState(storeState.currentTodo, newRepository); this.setState(newState); } handleValueChange(event: React.SyntheticEvent) { const todoText = (event.target as HTMLInputElement).value; this.setState(new TodoState(todoText, this.state.getRepository())); } handleClick() { TodoActionCreator.createTodo(this.state.currentTodo); } render(): JSX.Element { const todos = new TodoViewModelConverter(this.state.getRepository()).getTodoVMs(); return <div> <input type='input' value={this.state.currentTodo} onChange={this.handleValueChange.bind(this)}/> <button onClick={this.handleClick.bind(this)}>Update</button> <div> {todos.map((a) => { return <p key={a.key}>{a.text} : {a.dateString}</p>; })} </div> </div>; } }
ã³ã¼ãã¯Githubã«ããã¾ãããªãã¸ããªãã¡ã¢ãªçãªã®ã§ãããAPIçã«è¿ãã°è¤æ°ã¦ã¼ã¶ã§ç¹å®ã®ã¢ããªã±ã¼ã·ã§ã³ç¶æ ãå ±æã§ããããã«ãªãã¯ãã§ãã
以ä¸ãé·ããªãã¾ããããReactã§ã®Fluxã¨DDDã®çµ±åæ¹æ³ã®è§£èª¬ã§ããï¼çµ±åæ¹æ³ã¯ä»ã«ãããããããã¨æãã¾ãããåãèããä¸ä¾ã示ãã¦ã¿ã¾ããã
次ã¯Angular2çãã¾ã¨ãã¦ã¿ã¾ãã
*1:ä»åã¯Event Sourcingã§ã¯ããã¾ãããState Sourcingã§ããæ©ä¼ãããã°å®è£ ä¾ãä½ãã¾ã
*2:ActiveRecordã®ããã«ãã¡ã¤ã³ã¢ãã«ãæ°¸ç¶åæ©è½ãæããªãã®ã¯ãªããï¼ã¨ããçåãããããããã¾ããããã¡ã¤ã³ã¢ãã«ã¯ãã¸ãã¹ä¸ã®æ¦å¿µã¨ç´ä»ããã®ãªã®ã§ããªãã¹ãåºæã®æè¡ããä¾åæ§ãæé¤ããã¸ãã¹ä¸ã®ç¥èã表ç¾ãã¾ã
*3:Scalaã®ã±ã¼ã¹ã¯ã©ã¹ã®copyã¡ã½ãããã»ããâ¦ãã¤ã³ã¹ã¿ã³ã¹ã®å ¥ãæ¿ãã¨ãé¢åãªã®ã§