{"componentChunkName":"component---src-templates-post-template-js","path":"/posts/raspberrypi-clean-architecture","result":{"data":{"markdownRemark":{"id":"94d907e2-7781-58ce-98be-e90c85187bc0","html":"<p>最近、「Raspberry Pi でおもちゃの車を自動走行させる」という試みを始めました。</p>\n<p><a href=\"https://www.amazon.co.jp/dp/B081YD3VL5/ref=as_li_ss_il?ie=UTF8&linkCode=li2&tag=oshimayuki0d-22&linkId=573cedd3f64001e4e474596b9d5c2e51\" target=\"_blank\"><img border=\"0\" src=\"//ws-fe.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=B081YD3VL5&Format=_SL160_&ID=AsinImage&MarketPlace=JP&ServiceVersion=20070822&WS=1&tag=oshimayuki0d-22\" ></a><img src=\"https://ir-jp.amazon-adsystem.com/e/ir?t=oshimayuki0d-22&amp;l=li2&amp;o=9&amp;a=B081YD3VL5\" width=\"1\" height=\"1\" border=\"0\" alt=\"\" style=\"border:none !important; margin:0px !important;\"></p>\n<p><a href=\"https://www.amazon.co.jp/dp/B0761XVVJT/ref=as_li_ss_il?ie=UTF8&linkCode=li1&tag=oshimayuki0d-22&linkId=f118d0f77f7eb7dfda1b2b0e72e178de\" target=\"_blank\"><img border=\"0\" src=\"//ws-fe.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=B0761XVVJT&Format=_SL110_&ID=AsinImage&MarketPlace=JP&ServiceVersion=20070822&WS=1&tag=oshimayuki0d-22\" ></a><img src=\"https://ir-jp.amazon-adsystem.com/e/ir?t=oshimayuki0d-22&amp;l=li1&amp;o=9&amp;a=B0761XVVJT\" width=\"1\" height=\"1\" border=\"0\" alt=\"\" style=\"border:none !important; margin:0px !important;\"></p>\n<p>ゆくゆくは自動走行させたいのですが、まずはキーボード入力に応じて車が動くように進めています。</p>\n<p>こういったプログラムを書いていると、</p>\n<ul>\n<li>キーボードから入力を受け付ける処理</li>\n<li>車の動作アルゴリズムを提供する処理</li>\n<li>Raspberry Pi のピンに出力してモータを動かす処理</li>\n</ul>\n<p>などがごちゃごちゃになりやすいです。</p>\n<p>遊びなのでごちゃごちゃなコードでも構わないのですが、せっかくなので、クリーンアーキテクチャ的な考え方でコードを整理しました。</p>\n<p>※ この記事はコードの解説になるので、使った機器などは以下の記事を参照ください。</p>\n<ul>\n<li><a href=\"https://www.kanzennirikaisita.com/posts/raspberrypi-motordriver\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Raspberry Pi でモータドライバを使っておもちゃの車を走らせる</a></li>\n</ul>\n<h2 id=\"全体構成\" style=\"position:relative;\"><a href=\"#%E5%85%A8%E4%BD%93%E6%A7%8B%E6%88%90\" aria-label=\"全体構成 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>全体構成</h2>\n<p>整理したコードは、以下のような構成になりました。</p>\n<p><img src=\"/media/raspberrypi-clean-architecture.png\" alt=\"raspberrypi-clean-architecture\"></p>\n<p>ファイルの一覧は以下の通りです。</p>\n<div class=\"gatsby-highlight\" data-language=\"console\"><pre class=\"language-console\"><code class=\"language-console\">$ tree src/\nsrc/\n├── application\n│   └── raspberryPiCarApplication.ts\n├── domain\n│   ├── car.ts\n│   ├── carFactory.ts\n│   └── motor.ts\n├── index.ts\n├── infrastructure\n│   ├── dummy\n│   │   ├── dummyMotor.ts\n│   │   └── dummyMotorCarFactory.ts\n│   └── rpio\n│       ├── pwmMotor.ts\n│       └── pwmMotorCarFactory.ts\n├── logger.ts\n└── presentation\n    ├── dummyController.ts\n    └── keyboardController.ts</code></pre></div>\n<p>※ TypeScript で書いています。</p>\n<h2 id=\"コードの解説\" style=\"position:relative;\"><a href=\"#%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AE%E8%A7%A3%E8%AA%AC\" aria-label=\"コードの解説 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>コードの解説</h2>\n<p>ここから、各層のコードを見ていきます。</p>\n<h3 id=\"indexts\" style=\"position:relative;\"><a href=\"#indexts\" aria-label=\"indexts permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>index.ts</h3>\n<p>このアプリケーションは、以下のコマンドで起動することになります。</p>\n<div class=\"gatsby-highlight\" data-language=\"console\"><pre class=\"language-console\"><code class=\"language-console\">$ sudo node dist/index.js</code></pre></div>\n<p>そこでまず最初に、起動時のエンドポイントとなる index.ts を見てみます。</p>\n<p>index.ts の主要な処理は以下の内容だけになります。</p>\n<div class=\"gatsby-highlight\" data-language=\"typescript\"><pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">const</span> controller <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">KeyboardController</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">const</span> carFactory <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">PWMMotorCarFactory</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> app <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">RaspberryPiCarApplication</span><span class=\"token punctuation\">(</span>controller<span class=\"token punctuation\">,</span> carFactory<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\napp<span class=\"token punctuation\">.</span><span class=\"token function\">run</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>これは、RaspberryPiCarApplication を構築し、実行しているだけです。</p>\n<h3 id=\"application-層\" style=\"position:relative;\"><a href=\"#application-%E5%B1%A4\" aria-label=\"application 層 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>application 層</h3>\n<p>RaspberryPiCarApplication は、application 層で定義しています。</p>\n<div class=\"gatsby-highlight\" data-language=\"typescript\"><pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">export</span> <span class=\"token keyword\">default</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">RaspberryPiCarApplication</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">private</span> car<span class=\"token operator\">:</span> Car<span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">constructor</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">private</span> controller<span class=\"token operator\">:</span> ControllerPort<span class=\"token punctuation\">,</span> carFactory<span class=\"token operator\">:</span> CarFactory<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>car <span class=\"token operator\">=</span> carFactory<span class=\"token punctuation\">.</span><span class=\"token function\">create</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">run</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>controller<span class=\"token punctuation\">.</span><span class=\"token function\">enable</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>command<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">switch</span> <span class=\"token punctuation\">(</span>command<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">case</span> ControllerCommand<span class=\"token punctuation\">.</span>GoStraight<span class=\"token operator\">:</span>\n          <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>car<span class=\"token punctuation\">.</span><span class=\"token function\">goStraight</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">case</span> ControllerCommand<span class=\"token punctuation\">.</span>Stop<span class=\"token operator\">:</span>\n          <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>car<span class=\"token punctuation\">.</span><span class=\"token function\">stop</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">case</span> ControllerCommand<span class=\"token punctuation\">.</span>GoRight<span class=\"token operator\">:</span>\n          <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>car<span class=\"token punctuation\">.</span><span class=\"token function\">goRight</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">case</span> ControllerCommand<span class=\"token punctuation\">.</span>GoLeft<span class=\"token operator\">:</span>\n          <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>car<span class=\"token punctuation\">.</span><span class=\"token function\">goLeft</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">case</span> ControllerCommand<span class=\"token punctuation\">.</span>CleanUp<span class=\"token operator\">:</span>\n          <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>car<span class=\"token punctuation\">.</span><span class=\"token function\">cleanUp</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">default</span><span class=\"token operator\">:</span>\n          <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Error</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">Unexpected command: </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>command<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">cleanUp</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>car<span class=\"token punctuation\">.</span><span class=\"token function\">cleanUp</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>RaspberryPiCarApplication の run メソッドでは、Controller から受け取った入力に応じて、Car インスタンスの対応する処理を呼び出しています。</p>\n<p>application 層や domain 層は、ゲーム機で言うところの「本体」に相当します。</p>\n<p>そこに対してプラガブルにコントローラを指して使いたいので、コントローラの仕様は application 層で以下のように定義しています。</p>\n<div class=\"gatsby-highlight\" data-language=\"typescript\"><pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token comment\">// このアプリケーションが要求するコントローラの定義</span>\n\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">interface</span> <span class=\"token class-name\">ControllerPort</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">enable</span><span class=\"token punctuation\">(</span><span class=\"token function-variable function\">send</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>command<span class=\"token operator\">:</span> ControllerCommand<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token keyword\">void</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token keyword\">void</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">enum</span> ControllerCommand <span class=\"token punctuation\">{</span>\n  GoStraight<span class=\"token punctuation\">,</span>\n  Stop<span class=\"token punctuation\">,</span>\n  GoRight<span class=\"token punctuation\">,</span>\n  GoLeft<span class=\"token punctuation\">,</span>\n  CleanUp<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>ゲーム機のポートにコントローラを差し込んで使うイメージから、インタフェースには「ControllerPort」という名前をつけました。</p>\n<p>コントローラから受け取る信号は、「ControllerCommand」という型で定義しています。</p>\n<p>ControllerPort から接続したコントローラを有効化 (enable) すると、入力を受け取ったタイミングで RaspberryPiCarApplication クラス内に記述した処理が動き、車の操作に繋がるという仕組みです。</p>\n<h3 id=\"presentation-層\" style=\"position:relative;\"><a href=\"#presentation-%E5%B1%A4\" aria-label=\"presentation 層 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>presentation 層</h3>\n<p>ControllerPort に接続 (プログラミング的には implements) する KeyboardController は、presentation 層に配置しています。</p>\n<div class=\"gatsby-highlight\" data-language=\"typescript\"><pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">export</span> <span class=\"token keyword\">default</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">KeyboardController</span> <span class=\"token keyword\">implements</span> <span class=\"token class-name\">ControllerPort</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">enable</span><span class=\"token punctuation\">(</span><span class=\"token function-variable function\">send</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>command<span class=\"token operator\">:</span> ControllerCommand<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token keyword\">void</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">keypress</span><span class=\"token punctuation\">(</span>process<span class=\"token punctuation\">.</span>stdin<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    process<span class=\"token punctuation\">.</span>stdin<span class=\"token punctuation\">.</span><span class=\"token function\">on</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"keypress\"</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span>ch<span class=\"token punctuation\">,</span> key<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n      logger<span class=\"token punctuation\">.</span><span class=\"token function\">info</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">[keypress] ch = </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>ch<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">, key = </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span><span class=\"token constant\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token function\">stringify</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">)</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>key<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n\n      <span class=\"token comment\">// Ctrl + C</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">.</span>ctrl <span class=\"token operator\">&amp;&amp;</span> key<span class=\"token punctuation\">.</span>name <span class=\"token operator\">==</span> <span class=\"token string\">\"c\"</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        logger<span class=\"token punctuation\">.</span><span class=\"token function\">info</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Ctrl + C handling...\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token function\">send</span><span class=\"token punctuation\">(</span>ControllerCommand<span class=\"token punctuation\">.</span>CleanUp<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        process<span class=\"token punctuation\">.</span>stdin<span class=\"token punctuation\">.</span><span class=\"token function\">pause</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n\n      <span class=\"token keyword\">switch</span> <span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">.</span>name<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">case</span> <span class=\"token string\">\"up\"</span><span class=\"token operator\">:</span>\n          <span class=\"token function\">send</span><span class=\"token punctuation\">(</span>ControllerCommand<span class=\"token punctuation\">.</span>GoStraight<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">case</span> <span class=\"token string\">\"down\"</span><span class=\"token operator\">:</span>\n          <span class=\"token function\">send</span><span class=\"token punctuation\">(</span>ControllerCommand<span class=\"token punctuation\">.</span>Stop<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">case</span> <span class=\"token string\">\"right\"</span><span class=\"token operator\">:</span>\n          <span class=\"token function\">send</span><span class=\"token punctuation\">(</span>ControllerCommand<span class=\"token punctuation\">.</span>GoRight<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">case</span> <span class=\"token string\">\"left\"</span><span class=\"token operator\">:</span>\n          <span class=\"token function\">send</span><span class=\"token punctuation\">(</span>ControllerCommand<span class=\"token punctuation\">.</span>GoLeft<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n          <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    process<span class=\"token punctuation\">.</span>stdin<span class=\"token punctuation\">.</span><span class=\"token function\">setRawMode</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    process<span class=\"token punctuation\">.</span>stdin<span class=\"token punctuation\">.</span><span class=\"token function\">resume</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>キーボードの入力に応じて、RaspberryPiCarApplication 内に記述した処理が呼ばれるようになっています。</p>\n<p>現状はキーボード入力を扱う KeyboardController を使っていますが、今後例えば WebSocket などでリモートから命令を出すことなども考えられます。</p>\n<p>このコードのように presentation 層が application 層に依存するようにし、その逆向きの依存は排除することで、もしも別のコントローラを作成した場合でも application 層は書き換える必要がなくなります。</p>\n<p>WebSocket で操作したくなった場合は、ControllerPort を実装した WebSocketController を記述し、index.ts で KeyboardController を new している箇所を WebSocketController に置き換えるだけです。</p>\n<h3 id=\"domain-層\" style=\"position:relative;\"><a href=\"#domain-%E5%B1%A4\" aria-label=\"domain 層 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>domain 層</h3>\n<p>RaspberryPiCarApplication がコントローラからの入力を受け取った際に操作している Car クラスは、domain 層で定義されています。</p>\n<div class=\"gatsby-highlight\" data-language=\"typescript\"><pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">export</span> <span class=\"token keyword\">default</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">Car</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">constructor</span><span class=\"token punctuation\">(</span>\n    <span class=\"token keyword\">private</span> frontRightMotor<span class=\"token operator\">:</span> Motor<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">private</span> frontLeftMotor<span class=\"token operator\">:</span> Motor<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">private</span> backRightMotor<span class=\"token operator\">:</span> Motor<span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">private</span> backLeftMotor<span class=\"token operator\">:</span> Motor\n  <span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">goStraight</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token operator\">:</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">goRight</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token operator\">:</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">goLeft</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token operator\">:</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">stop</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token operator\">:</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token operator\">:</span></code></pre></div>\n<p>Car クラスは、4 つのモータ (Motor) から成ります。</p>\n<p>直進、右折、左折、停止などのメソッドを持ち、その中でモータを操作しているわけです。</p>\n<p>domain 層において、モータは interface だけ定義しています。</p>\n<div class=\"gatsby-highlight\" data-language=\"typescript\"><pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">export</span> <span class=\"token keyword\">default</span> <span class=\"token keyword\">interface</span> <span class=\"token class-name\">Motor</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">changeToTopSpeed</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token keyword\">void</span><span class=\"token punctuation\">;</span>\n  <span class=\"token function\">changeToMiddleSpeed</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token keyword\">void</span><span class=\"token punctuation\">;</span>\n  <span class=\"token function\">stop</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token keyword\">void</span><span class=\"token punctuation\">;</span>\n  <span class=\"token function\">cleanUp</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token keyword\">void</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>これは、domain 層を Rapsberry Pi のピンへの出力という技術詳細に依存させないためです。</p>\n<p>こうすることで、ピンへの出力をモックしたテストが記述しやすくなったりもします。</p>\n<h3 id=\"infrastructure-層\" style=\"position:relative;\"><a href=\"#infrastructure-%E5%B1%A4\" aria-label=\"infrastructure 層 permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>infrastructure 層</h3>\n<p>Motor の具体実装は、infrastructure 層に配置しています。</p>\n<div class=\"gatsby-highlight\" data-language=\"typescript\"><pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">export</span> <span class=\"token keyword\">default</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">PWMMotor</span> <span class=\"token keyword\">implements</span> <span class=\"token class-name\">Motor</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">protected</span> pwmValue<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">constructor</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">protected</span> pin<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token operator\">:</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">changeToTopSpeed</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">changeSpeed</span><span class=\"token punctuation\">(</span><span class=\"token constant\">MAX_SPEED_VALUE</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">changeToMiddleSpeed</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">changeSpeed</span><span class=\"token punctuation\">(</span><span class=\"token constant\">MIDDLE_SPEED_VALUE</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">stop</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">changeSpeed</span><span class=\"token punctuation\">(</span><span class=\"token constant\">STOP_SPEED_VALUE</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token operator\">:</span></code></pre></div>\n<p>Rapsberry Pi のピンからの出力の具体的な内容は、このクラスだけが知っています。</p>\n<h2 id=\"まとめ\" style=\"position:relative;\"><a href=\"#%E3%81%BE%E3%81%A8%E3%82%81\" aria-label=\"まとめ permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>まとめ</h2>\n<p>構成図を再掲します。</p>\n<p><img src=\"/media/raspberrypi-clean-architecture.png\" alt=\"raspberrypi-clean-architecture\"></p>\n<p>このようにコードを整理することで、</p>\n<ul>\n<li>キーボードから入力を受け付ける処理</li>\n<li>車の動作アルゴリズムを提供する処理</li>\n<li>Raspberry Pi のピンに出力してモータを動かす処理</li>\n</ul>\n<p>などが分離され、コントローラやモータといった技術詳細がプラガブルになりました。</p>\n<p>もともとここまで整理するつもりはなかったのですが、</p>\n<ul>\n<li>コントローラをプラガブルにする</li>\n<li>モータをプラガブルにする</li>\n</ul>\n<p>ことを満たすようにコードを整理していたところ、自然にこれに近いかたちになったので、せっかくなのでこの状態まで持ってきてみました。</p>\n<p>この規模だとここまでする必要はないかもしれませんが、アプリケーション設計の練習としては面白いと思います。</p>\n<h2 id=\"ソースコード\" style=\"position:relative;\"><a href=\"#%E3%82%BD%E3%83%BC%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%89\" aria-label=\"ソースコード permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>ソースコード</h2>\n<p>ソースコードの全容は GitHub の以下のページで公開しています。</p>\n<ul>\n<li><a href=\"https://github.com/os1ma/raspberrypi-car\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">os1ma/raspberrypi-car</a></li>\n</ul>","fields":{"slug":"/posts/raspberrypi-clean-architecture","tagSlugs":["/tag/raspberrypi/","/tag/architecture/"],"autoRecommendPosts":["raspberrypi-motordriver","raspberrypi-k3s-non-stop-release","typescript-books","what-is-service-class"]},"frontmatter":{"date":"2021-05-22T14:33:22.213Z","description":"最近、「Raspberry Pi でおもちゃの車を自動走行させる」という試みを始めました。\nこういったプログラムを書いていると、「キーボードから入力を受け付ける処理」、「車の動作アルゴリズムを提供する処理」、「Raspberry Pi のピンに出力してモータを動かす処理」などがごちゃごちゃになりやすいです。\n遊びなのでごちゃごちゃなコードでも構わないのですが、せっかくなので、クリーンアーキテクチャ的な考え方でコードを整理しました。","tags":["raspberrypi","architecture"],"title":"Raspberry Pi で動かすコードをクリーンアーキテクチャ的な考え方で整理する","socialImage":null,"recommendPosts":["https://www.kanzennirikaisita.com/posts/raspberrypi-motordriver","https://www.kanzennirikaisita.com/posts/raspberrypi-k3s-non-stop-release","https://www.kanzennirikaisita.com/posts/software-developer-books-index"]}}},"pageContext":{"slug":"/posts/raspberrypi-clean-architecture"}},"staticQueryHashes":["251939775","3942705351","401334301"]}