<?xml version='1.0' encoding='UTF-8'?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title>ソフトウェアアーキテクチャ · Tommy Cheese</title>
    <link>https://tommycheese.github.io/ja/tags/%E8%BD%AF%E4%BB%B6%E6%9E%B6%E6%9E%84/</link>
    <description>ソフトウェア工学、人工知能、学びと実践について綴る Tommy Cheese の個人ブログ。</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>ja</language>
    <lastBuildDate>Mon, 29 Jul 2024 06:13:15 +0800</lastBuildDate>
    <atom:link href="https://tommycheese.github.io/ja/tags/%E8%BD%AF%E4%BB%B6%E6%9E%B6%E6%9E%84/index.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>『クリーンアーキテクチャ』再読（6）：境界</title>
      <link>https://tommycheese.github.io/ja/blogs/%E5%86%8D%E8%AF%BB%E6%95%B4%E6%B4%81%E6%9E%B6%E6%9E%84%E4%B9%8B%E9%81%93%E5%85%AD/</link>
      <pubDate>Mon, 29 Jul 2024 06:13:15 +0800</pubDate>
      <guid>https://tommycheese.github.io/ja/blogs/%E5%86%8D%E8%AF%BB%E6%95%B4%E6%B4%81%E6%9E%B6%E6%9E%84%E4%B9%8B%E9%81%93%E5%85%AD/</guid>
      <description>&lt;h2 id="什么是边界"&gt;境界とは何か&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;ソフトウェアアーキテクチャの設計は、それ自体が境界を引く技術です。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;境界の役割は、ソフトウェアをさまざまな要素に分け、境界の両側の依存関係を制約することです。プロジェクトの初期に境界を引くと、一部の意思決定をできるだけ先送りし、将来その決定がシステムの&lt;strong&gt;中核となる業務ロジック&lt;/strong&gt;を妨げないようにできます。境界を引くことは、アーキテクチャ内の不要な結合を減らす、またはなくすことにも役立ちます。システムの結合、とりわけ早すぎる未熟な意思決定による結合は、最も人的資源を消費する部分です。採用するフレームワークやデータベースなどの詳細は、そのような決定の例であり、後回しにすべきです。&lt;/p&gt;
&lt;p&gt;では、境界はどのように引けばよいのでしょうか。次の基本原則に従えます：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;境界は、互いに無関係なものの間に引きます。たとえばGUIと業務ロジックです。入出力であるGUIは業務ロジックにとって本質的ではなく、業務ロジックにはさまざまなGUI実装を組み合わせられます。&lt;/li&gt;
&lt;li&gt;境界線は、システムの変更の軸に沿って引きます。境界の両側にあるコンポーネントは、異なる理由で、異なる頻度で変化するべきです。その軸を判断する指針には、モジュール／クラスレベルのSRPとコンポーネントレベルのCCPがあります。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;境界を引くことの重要性と、その考え方がわかったので、次は実際に境界を引く基本的な手順を考えます：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;まずシステムをコンポーネントに分割します。一方は中核となる業務ロジックのコンポーネント、もう一方は中核業務とは無関係ですが、必要な機能を提供するプラグインです。&lt;/li&gt;
&lt;li&gt;ソースコードを変更し、中核ではないコンポーネントが、中核の業務ロジックのコンポーネントに依存するようにします。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;境界を引くことは、依存性逆転の原則と安定度・抽象度の原則を具体的に適用することです。依存関係の矢印は、低レベルの具象実装から高レベルの抽象へ向かうべきです。&lt;/p&gt;
&lt;h2 id="边界剖析"&gt;境界を詳しく見る&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;システムのアーキテクチャは、一連のソフトウェアコンポーネントと、それらの間にある境界によって定義されます&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;境界にはさまざまな形があります。この章では、境界を越える呼び出しを例に、その構造を考えます。&lt;/p&gt;
&lt;p&gt;実行時の境界を越える呼び出しとは、境界線の片側にある関数が、もう片側の関数を呼び出し、同時にデータを渡すことです。これを適切に構成するには、ソースコードの依存関係を適切に管理する必要があります。管理しなければ、一つのモジュールのソースコードが変わるだけで、他のモジュールのソースコードも変更や再コンパイルが必要になり、再デプロイしなければならないことがあります。境界を明確にすることは、このような事態を減らすのに役立ちます。&lt;/p&gt;
&lt;p&gt;境界を越える呼び出しには、ソースコードレベル、デプロイレベル、サービスレベル、そして物理的な境界という分類があります。&lt;/p&gt;
&lt;h3 id="源码层次的跨边界调用"&gt;ソースコードレベルの境界を越える呼び出し&lt;/h3&gt;
&lt;p&gt;ソースコードレベルの境界を越える呼び出しは、モノリシックアーキテクチャに見られます。この種のアーキテクチャでは、一般に何らかの動的なポリモーフィズムを使って内部の依存関係を管理する必要があります。&lt;/p&gt;
&lt;p&gt;最も単純なのは、低レベルのクライアントが高レベルのサービス関数を呼び出す形です。この依存関係の向きは、実行時とコンパイル時で一致し、どちらも低レベルのコンポーネントから高レベルのコンポーネントへ向かいます。&lt;/p&gt;
&lt;p&gt;（元の図は現在ありません）&lt;/p&gt;
&lt;p&gt;一方、高レベルのコンポーネントにあるクライアントが、低レベルのコンポーネントにあるサービスを呼び出す場合は、動的なポリモーフィズムを使って依存関係を逆転させる必要があります。図のServiceインターフェースは、ある種のSPIです。&lt;/p&gt;
&lt;p&gt;（元の図は現在ありません）&lt;/p&gt;
&lt;h3 id="部署层次的跨边界调用"&gt;デプロイレベルの境界を越える呼び出し&lt;/h3&gt;
&lt;p&gt;デプロイレベルの境界を越える呼び出しでは、デプロイ可能な各単位を、扱いやすいファイル形式にまとめます。この点を除けば、デプロイレベルで分離されたコンポーネントは、モノリシックな構造とほぼ同じです。&lt;/p&gt;
&lt;p&gt;デプロイレベルの境界を越える呼び出しには、物理的な境界も関わります。別々にデプロイされた単位が通信するには、物理的な境界を越える必要があるためです。一般的な物理的境界には、動的リンクライブラリ、スレッド、ローカルプロセスなどがあります。&lt;/p&gt;
&lt;h3 id="服务层次的跨边界调用"&gt;サービスレベルの境界を越える呼び出し&lt;/h3&gt;
&lt;p&gt;サービスは、システムアーキテクチャで最も強い境界の形です。一つのサービスは一つのプロセスです。アーキテクチャの境界を引く際には、通信回数をできるだけ抑える必要があります。このレベルの通信は、大きな遅延に対応できなければなりません。それ以外は、ローカルプロセスと同じルールをサービスレベルにも適用できます。つまり、低レベルのサービスを高レベルのサービスの「プラグイン」にします。&lt;/p&gt;
</description>
    <category>ソフトウェアアーキテクチャ</category><category>クリーンアーキテクチャ</category></item>
    <item>
      <title>『クリーンアーキテクチャ』再読（5）：ソフトウェアアーキテクチャ</title>
      <link>https://tommycheese.github.io/ja/blogs/%E5%86%8D%E8%AF%BB%E6%95%B4%E6%B4%81%E6%9E%B6%E6%9E%84%E4%B9%8B%E9%81%93%E4%BA%94/</link>
      <pubDate>Sun, 28 Jul 2024 19:23:23 +0800</pubDate>
      <guid>https://tommycheese.github.io/ja/blogs/%E5%86%8D%E8%AF%BB%E6%95%B4%E6%B4%81%E6%9E%B6%E6%9E%84%E4%B9%8B%E9%81%93%E4%BA%94/</guid>
      <description>&lt;p&gt;いよいよソフトウェアアーキテクチャに進みます。これまではモジュール／クラスやコンポーネントを紹介しました。この回では、ソフトウェアアーキテクチャの基本的な定義を中心に説明します。&lt;/p&gt;
&lt;h2 id="写在最前面"&gt;最初に伝えたいこと&lt;/h2&gt;
&lt;p&gt;🌟ソフトウェアアーキテクト自身がプログラマーであり、現場でコードを書き続ける必要があります。システム設計がもたらす問題を自分で引き受けなければ、設計の悪さによる苦痛を実感できず、やがて正しい設計の方向を見失うからです。&lt;/p&gt;
&lt;h2 id="什么是软件架构"&gt;ソフトウェアアーキテクチャとは何か&lt;/h2&gt;
&lt;p&gt;ソフトウェアアーキテクチャの仕事の本質は、システムをどのようにコンポーネントへ分割し、その配置関係や通信方法をどうするかを検討、計画することです。つまり、境界を決めることです。&lt;/p&gt;
&lt;p&gt;ソフトウェアアーキテクチャを設計する目的は、一般に二つあります：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;コンポーネントの開発、デプロイ、実行、保守を、日々の仕事の中でより円滑に行えるようにすること。&lt;/li&gt;
&lt;li&gt;さまざまな作業を進めやすいシステムを設計するために、設計の中でできるだけ長い間、&lt;strong&gt;できるだけ多くの選択肢を残すこと&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;ソフトウェアシステムのアーキテクチャの品質と、システムが正常に動くかどうかという振る舞いには、それほど強い関係はありません。設計はひどくても正常に動くシステムは、世の中にたくさんあります。本当の問題は、開発、デプロイ、その後の追加開発で現れがちです。これも、アーキテクチャの価値はある程度、振る舞いの価値より大きいという著者の考えを示しています。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;ソフトウェアアーキテクチャ設計の目標は、次のとおりです：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;中核的な目標：良いアーキテクチャはユースケースを中心に設計されるべきです。ユースケースに集中し、他の周辺要素から隔離します。そのためには選択をできる限り先送りし、選択肢を多く残します。&lt;/li&gt;
&lt;li&gt;主な目標：ソフトウェアシステムのライフサイクル全体を支えることです。適切なアーキテクチャは、システムを理解しやすく、変更しやすく、保守しやすくし、簡単にデプロイできるようにします。&lt;/li&gt;
&lt;li&gt;究極の目標：プログラマーの生産性を最大化し、その力を解放すると同時に、システムの&lt;strong&gt;総&lt;/strong&gt;運用コストを最小化することです。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;アーキテクチャ設計で重視するのは、&lt;strong&gt;方針&lt;/strong&gt;を互いに分離し、変更のされ方に応じて再びグループ化すること、つまり境界を引くことです。この際の指針には、SRP、CCP、CSP、SDP、SAPなどがあります。&lt;/p&gt;
&lt;h2 id="软件架构的职责"&gt;ソフトウェアアーキテクチャの責務&lt;/h2&gt;
&lt;p&gt;ソフトウェアアーキテクチャは、開発、デプロイ、実行、保守という複数の側面で責務を持ちます。&lt;/p&gt;
&lt;p&gt;開発の面では、アーキテクチャはソフトウェアシステムを開発しやすくする必要があります。そのため、チームが異なれば採用するアーキテクチャ設計も異なります。これは、ある意味でコンウェイの法則を表しています。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;コンウェイの法則は、なぜアーキテクチャにチーム構造が反映されることを説明できるのでしょうか。詳しくは動画をご覧ください：&lt;a href="https://www.bilibili.com/video/BV1bb421E7i6/?spm_id_from=333.337.search-card.all.click&amp;amp;vd_source=8c0f6bcaf6b2e92f574553f45e565994"&gt;コンウェイの法則：なぜアーキテクチャにはチーム構造が反映されるのか？—bilibili&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;デプロイの面では、ワンクリックで簡単にソフトウェアをデプロイできるようにする必要があります。&lt;/p&gt;
&lt;p&gt;実行の面では、&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;前述のとおり、効率的な実行に対するアーキテクチャの影響は、他の側面より小さいとされています。著者はその主な理由を、実行面での配慮不足はハードウェアの追加で補える一方、アーキテクチャ設計には通常それより高価な人的資源が必要だからだと考えています。&lt;/li&gt;
&lt;li&gt;効率的な実行に寄与するだけでなく、アーキテクチャはシステムの実行時の要件も表すべきです。適切に設計されたアーキテクチャなら、開発者はシステムの動作を一目で理解できます。アーキテクチャは実行過程を明らかにし、ユースケース、機能、必須の振る舞いを、開発者に見える第一級の要素として扱うことで、システムを理解しやすくするべきです。「叫ぶアーキテクチャ」の章は、この点を重点的に説明しています。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;保守の面では、ソフトウェアシステムの保守コストは通常最も高くなります。そのコストは、&lt;strong&gt;探索&lt;/strong&gt;と&lt;strong&gt;リスク&lt;/strong&gt;の二つに分けられます：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;探索：探索（spelunking）のコストは、既存のソフトウェアシステムを調べ、新機能の追加や問題の修正を行う最適な場所と方法を見つける作業から生じます。&lt;/li&gt;
&lt;li&gt;リスク：リスク（risk）は、これらの変更によって新しい問題を生む可能性を指します。その可能性がリスクのコストです。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="保持可选项"&gt;選択肢を残す&lt;/h2&gt;
&lt;p&gt;以前紹介した、ソフトウェアの「振る舞いの価値」と「アーキテクチャの価値」を思い出してください。アーキテクチャの価値を高めるにはソフトウェアをより柔軟にし、そのためにはアーキテクチャにできるだけ多くの選択肢を残します。&lt;/p&gt;
&lt;p&gt;ソフトウェアシステムの要素は、&lt;strong&gt;方針&lt;/strong&gt;と&lt;strong&gt;詳細&lt;/strong&gt;に分けられます。方針は、ソフトウェア内のすべての業務ルールと処理手順を表し、システムの本当の価値を担います。詳細は、利用者、他のシステム、プログラマーが方針とやり取りするために必要ですが、方針そのものには影響しない振る舞いを指します。IOデバイスやデータベースなどが例です。こうした詳細については、できるだけ選択肢を残すべきです。&lt;/p&gt;
&lt;p&gt;ソフトウェアアーキテクトの目標は、方針を&lt;strong&gt;最も基本的な要素&lt;/strong&gt;とし、詳細を方針から切り離したシステムの形を作ることです。これにより、具体的な意思決定で&lt;strong&gt;詳細に関する決定を先送りできます&lt;/strong&gt;。プロジェクトの後半になるほど、妥当な判断のための情報が増えるからです。優れたアーキテクトは、選択肢の数を最大化することに力を注ぐべきです。&lt;/p&gt;
&lt;p&gt;著者は、詳細の決定を先送りする重要性をデバイス独立性の例で説明しています。現代のOSには多種多様な入出力デバイスがありますが、コンピューターの初期には、穿孔紙テープが主流で、ほぼ唯一の入出力手段でした。プログラマーは自然に、紙テープを読み書きするコードをシステムコードへ結合させていました。磁気テープが登場すると、その入出力に対応する新しいコードが必要になり、光ディスクが登場するとまた同じことが起きました。この重複開発と適応性の低さに対応するため、デバイス独立性という考え方が生まれました。デバイスを関数として抽象化し、OSは関数を通じて入出力デバイスとやり取りし、開発者はその具体的な実装だけを提供します。こうして入出力はOSのプラグインになりました。これは、オープン・クローズドの原則の原型でもあります。「追加を歓迎👏し、変更に抗う🥊！」ということです。&lt;/p&gt;
&lt;h2 id="保持独立性"&gt;独立性を保つ&lt;/h2&gt;
&lt;p&gt;良いソフトウェアアーキテクチャには、十分な独立性が必要です。&lt;/p&gt;
&lt;p&gt;結合を解くことで、アーキテクチャの独立性を確保できます。&lt;/p&gt;
&lt;p&gt;分離には、水平方向と垂直方向があります。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;水平方向の分離（レイヤーごとの分離）：システムをUI、データベースなど、複数の水平レイヤーに分けます。&lt;/li&gt;
&lt;li&gt;ユースケースの分離（垂直方向の分離）：水平レイヤーを分離すると同時に、ユースケースごとに垂直方向にも分割します。たとえば、注文を追加するユースケースのUIと、注文を削除するユースケースのUIを分けます。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;変更理由の違いに応じてシステムを分離すれば、既存のユースケースに影響を与えずに、新しいユースケースを追加し続けられます。&lt;/p&gt;
&lt;p&gt;分離を行うレベルも選べます。ソースコード、デプロイ、サービスという三つのレベルがあります。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ソースコードレベル：モジュール間のソースコードの依存関係を制御し、コンポーネントは関数呼び出しでやり取りします。この形をモノリシック構造と呼びます。&lt;/li&gt;
&lt;li&gt;デプロイレベル：Jarファイルなどのデプロイ単位間の依存関係を制御します。コンポーネントは、スレッド間通信（ネットワーク越しではない点に注意）、Socket通信、共有メモリによる通信などを使います。&lt;/li&gt;
&lt;li&gt;サービスレベル：コンポーネント間の依存をデータ構造のレベルまで減らし、ネットワークパケットで通信します。マイクロサービスがrpcやrestでやり取りする形などが例です。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;どの分離レベルが優れているかを決める厳密な基準はありません。プロジェクトの成熟に伴って、最適な方式は変わり得るからです。適切に設計されたアーキテクチャなら、一つのファイルとしてデプロイするモノリシック構造から始め、独立した複数のデプロイ単位、さらには独立したサービスやマイクロサービスへ成長できます。そして状況が変われば、再びモノリシック構造へ段階的に戻せます。この過程でも、システムのソースコードの大部分は変更の影響から守られるべきです。システム全体にとって、&lt;strong&gt;分離の方式も選択肢の一つであるべきです&lt;/strong&gt;。大規模なデプロイではある方式を使い、小規模なデプロイでは別の方式を使えます。&lt;/p&gt;
&lt;p&gt;分離について理解したので、システムの独立性にどのような影響を与えるかを見てみましょう。&lt;/p&gt;
&lt;h3 id="解耦对系统运行独立性的意义"&gt;実行の独立性に対する分離の意義&lt;/h3&gt;
&lt;p&gt;異なる側面のユースケースが適切に分離されていれば、高スループットが必要なユースケースと低スループットでよいユースケースは自然に分かれます。UIやデータベースを業務ロジックから分離できれば、それぞれを別のサーバーで動かせます。大きな帯域を必要とするアプリケーションは、複数のサーバーで複数のインスタンスを動かせます。&lt;/p&gt;
&lt;h3 id="解耦对系统开发独立性的意义"&gt;開発の独立性に対する分離の意義&lt;/h3&gt;
&lt;p&gt;システムが水平レイヤーとユースケースに従って適切に分離されていれば、アーキテクチャは複数チームでの開発を支えられます。チームの分担が機能単位、コンポーネント単位、レイヤー単位、あるいは別の軸であっても対応できます。&lt;/p&gt;
&lt;h3 id="解耦对系统部署独立性的意义"&gt;デプロイの独立性に対する分離の意義&lt;/h3&gt;
&lt;p&gt;適切に分離できていれば、システムの実行中に各レイヤーの実装やユースケースをホットスワップできます。この場合、新しいユースケースを追加するには、新しいjarファイルを置くか、いくつかのサービスを起動するだけで済み、それ以外の部分は一切影響を受けません。&lt;/p&gt;
&lt;h2 id="什么是代码重复"&gt;コードの重複とは何か&lt;/h2&gt;
&lt;p&gt;コードの重複には二種類あります：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;本当の重複：コードから取り除くべき部分です。&lt;/li&gt;
&lt;li&gt;見かけ上の重複：同じように見えても、実際には異なる進化の道筋を持ち、変更の頻度や境界も異なるコードです。CRPは、利用頻度が異なるコードを別のコンポーネントへ分けるよう求めます。この点を考慮せずに読むと、見かけ上の重複を本当の重複と誤認しかねません。このような「重複」は必要な場合があります。給与計算の例を覚えていますか。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;（第五回・完）&lt;/p&gt;
</description>
    <category>ソフトウェアアーキテクチャ</category><category>クリーンアーキテクチャ</category></item>
    <item>
      <title>ドメイン駆動設計入門</title>
      <link>https://tommycheese.github.io/ja/blogs/ddd%E9%A2%86%E5%9F%9F%E9%A9%B1%E5%8A%A8%E8%AE%BE%E8%AE%A1%E5%88%9D%E8%AF%86/</link>
      <pubDate>Fri, 26 Jul 2024 19:49:45 +0800</pubDate>
      <guid>https://tommycheese.github.io/ja/blogs/ddd%E9%A2%86%E5%9F%9F%E9%A9%B1%E5%8A%A8%E8%AE%BE%E8%AE%A1%E5%88%9D%E8%AF%86/</guid>
      <description>&lt;h1 id="领域驱动设计概述"&gt;ドメイン駆動設計の概要&lt;/h1&gt;
&lt;p&gt;ドメイン駆動設計（DDD, Domain-Driven Design）は、モデルを中心とした設計手法です。ドメインモデルでドメインの知識を捉え、そのモデルを使って&lt;strong&gt;保守しやすい&lt;/strong&gt;ソフトウェアを構築します。&lt;/p&gt;
&lt;p&gt;DDDの設計は、戦略的設計と戦術的設計に分かれます。戦略的設計ではドメイン、サブドメイン、境界づけられたコンテキストを設計し、戦術的設計ではエンティティ、値オブジェクト、ドメインイベントなどを設計します。その関係は次のとおりです：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tommycheese.github.io/blogimages/DDD1.png" alt="img"&gt;&lt;/p&gt;
&lt;h2 id="战略设计阶段相关概念"&gt;戦略的設計に関する概念&lt;/h2&gt;
&lt;h3 id="领域"&gt;ドメイン&lt;/h3&gt;
&lt;p&gt;ドメインとは、システムが解決する問題の領域です。たとえば、商品情報管理はシステムが扱うドメインの一つになります。&lt;/p&gt;
&lt;h3 id="子域"&gt;サブドメイン&lt;/h3&gt;
&lt;p&gt;使われる言葉の違いに基づき、ドメインを次のようなサブドメインに分けられます：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;コアドメイン：製品の競争力を決めるサブドメインです。最も重要で、業務の中核にあり、独自性を持つ部分です。&lt;/li&gt;
&lt;li&gt;汎用サブドメイン：複数のサブドメインで使う共通機能です。認証や権限管理などの汎用システムが例です。企業固有の事情に左右されにくく、大幅なカスタマイズは必要ありません。&lt;/li&gt;
&lt;li&gt;支援サブドメイン：コア機能でも汎用機能でもないサブドメインです。企業固有の特徴はありますが、汎用性はありません。コード値を扱うデータディクショナリなどが例です。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="限界上下文"&gt;境界づけられたコンテキスト&lt;/h3&gt;
&lt;p&gt;境界づけられたコンテキストとは、異なる問題を異なるモデルで解決するために区切った、それぞれの範囲です。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;境界づけられたコンテキストは、一つまたは複数のサブドメインの集合です&lt;/strong&gt;。&lt;strong&gt;一つのコンテキストで、完結した業務フローを支えられるようにします&lt;/strong&gt;。また、&lt;strong&gt;その業務フローに関係するドメインを、一つのコンテキストに収めます&lt;/strong&gt;。境界づけられたコンテキストはマイクロサービスを分割する基準となり、各コンテキストが一つのマイクロサービスに対応します。&lt;/p&gt;
&lt;p&gt;では、なぜコンテキストを区切る必要があるのでしょうか。それは、&lt;strong&gt;文脈&lt;/strong&gt;の存在がコーディングを難しくするからです。例を挙げてみましょう：&lt;/p&gt;
&lt;p&gt;採用のドメインでは「プラットフォーム」という言葉で、出身の学校や組織を表すことがあります。飛び込み競技では、選手が飛び込む台を指します。鉄道輸送のドメインでは、駅のホームを表すかもしれません。ある日、飛び込み選手、人事担当者、乗務員が偶然集まり、お互いの職業を知らないまま、人事担当者が選手に「どのプラットフォームの出身ですか」と尋ねたら……。&lt;/p&gt;
&lt;p&gt;同じ「プラットフォーム」という言葉でも、三人の間に&lt;strong&gt;取り決めがなければ&lt;/strong&gt;、理解が異なる可能性があります。この問題を解決するには、飛び込み、採用、交通の各ドメインを分離し、それぞれで用語を定義します。一つのドメイン内で話せば、曖昧さをできる限り避けられます。これが取り決めです。業務に関係するドメインを一つのコンテキストに定義するのがよい理由も、曖昧さを減らすためです。&lt;/p&gt;
&lt;p&gt;ソフトウェアシステムの設計でも、同じような問題が起こります。飛び込み、採用、乗車の各サービスを含む大きな製品を設計しているとします。サービスを一切分割せず、飛び込みの業務ではplatformというデータ構造を飛び込み台に、採用では組織に、交通では駅のホームに使ったら、三つの業務が交わるときに問題が起きます。意味は違うのにほぼ同じ名前の変数が画面いっぱいに並んだら、どうすればよいでしょうか。&lt;/p&gt;
&lt;p&gt;あるドメインの用語の意味を明確に限定すれば、そのドメイン内では迷わずに用語を使えます。これが境界づけられたコンテキストの重要な役割です。&lt;/p&gt;
&lt;h2 id="战术设计阶段相关概念"&gt;戦術的設計に関する概念&lt;/h2&gt;
&lt;h3 id="值对象和实体"&gt;値オブジェクトとエンティティ&lt;/h3&gt;
&lt;p&gt;まず、値オブジェクトとエンティティを理解しましょう。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;値オブジェクトは、属性の値によって識別されるオブジェクトです。二つの値オブジェクトの内部の値がすべて同じなら、同じ値オブジェクトだとみなします。値オブジェクトの属性は不変です。&lt;/li&gt;
&lt;li&gt;エンティティは、一意な識別子と状態を持ち、ライフサイクルのある業務オブジェクトです。属性は変更できます。二つのエンティティの属性が完全に同じでも、それだけでは同じエンティティとはみなしません。IDなどの識別子が同じ場合にだけ、同じエンティティだとみなします。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;例で値オブジェクトとエンティティを区別しましょう：&lt;/p&gt;
&lt;p&gt;白色を表す値オブジェクトColor white{R:255, G:255, B:255}と、タイヤのエンティティtire{Air:&lt;em&gt;,Size:&lt;/em&gt;}があるとします：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;可変性：白色の各属性は不変です。一つでも変えると、そのオブジェクトはもう「白色」を表さなくなるからです。一方、タイヤの空気圧やサイズは変えられます。これらの値が変わっても、タイヤであることには変わりません。&lt;/li&gt;
&lt;li&gt;比較可能性：値オブジェクトは値が不変だからこそ、等価性の規則を持ちます。二つの白色オブジェクトの値は必ず同じなので、内部の値がすべて同じなら、同じ値オブジェクトだとみなせます。エンティティは属性が可変なので、この性質を持ちません。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;エンティティの形には、一般に四つあります：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;失血モデル：データの定義とgetter/setterメソッドだけを持ち、業務ロジックとアプリケーションロジックはサービス層に置きます。このようなクラスはJavaではPOJOと呼ばれます。&lt;/li&gt;
&lt;li&gt;貧血モデル：一部の業務ロジックを持ちますが、永続化層に依存する業務ロジックは含みません。その部分はサービス層に置きます。&lt;/li&gt;
&lt;li&gt;充血モデル：永続化層に依存するものも含め、すべての業務ロジックを持ちます。&lt;/li&gt;
&lt;li&gt;膨張モデル：認可やトランザクションなど、業務ロジックに直接関係しないアプリケーションロジックまで、すべてドメインモデルに置きます。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;リポジトリ Repo&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Repoは、Entityのために設計する保存操作です。Repoの操作はできるだけ低レベルにし、短い名前を付け、数を増やしすぎないようにします。JavaのMyBatis Mapperは、Repoの実装の一種とみなせます。&lt;/p&gt;
&lt;h3 id="聚合与聚合根"&gt;集約と集約ルート&lt;/h3&gt;
&lt;p&gt;集約は、より大きな範囲のカプセル化です。同じライフサイクルを持ち、業務上切り離せないエンティティと値オブジェクトをまとめて扱います。外部に参照を公開できるのは集約ルートだけであり、集約は凝集性の表れでもあります。集約ルート同士が呼び合うこともできます。集約ルートを抽象化した名前は、通常は名詞になります。&lt;/p&gt;
&lt;p&gt;集約ルートは、次の方法でカプセル化を実現します：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;集約全体への操作は、必ず集約ルートを通して行い、外部から内部の要素を直接操作することは許しません。たとえば、紫色の外観＋タイヤ＋スチールフレーム＋……＝自動車と考えます。車を運転するとき、タイヤ、ハンドル、外観などを個別の独立した要素として操作するのではなく、自動車という集約ルートを操作し、他のエンティティや値オブジェクトを間接的に扱います。&lt;/li&gt;
&lt;li&gt;集約は一組の&lt;strong&gt;境界&lt;/strong&gt;を定義し、その内側のすべての要素が、業務ロジックに照らして有効である必要があります。&lt;/li&gt;
&lt;li&gt;集約は、一つの原子的なトランザクション内で操作しなければなりません。そうでないと不整合が起こり得ます。集約が操作の単位であり、リポジトリから取り出し、操作を終えて戻すまでが一つの原子的な操作です。原子性とは何でしょうか。自動車の集約ルートに車輪やスチール／アルミ／カーボンのフレームのエンティティがあるとして、車輪を一つ外して点検した後、そのまま取り付けずに終えることはできない、ということです。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="领域事件domain-events"&gt;ドメインイベント Domain Events&lt;/h3&gt;
&lt;p&gt;エンティティの属性が変わると、ドメインイベントが発生します。ドメインイベントとは、ドメインの専門家が重要だと考える出来事です。ドメイン内で起きた、注目すべきイベントであり、通常はドメインオブジェクトの状態変化を意味します。&lt;strong&gt;ドメインイベントは、システム内でメッセージを伝え、他のアクションを起動する役割を持ち、ドメインモデルを疎結合にする重要な手段の一つです&lt;/strong&gt;。イベントの伝達には、よく&lt;strong&gt;メッセージキュー&lt;/strong&gt;を使います。そのメッセージを購読するすべてのサブドメインが、それぞれ内部で応答処理を行います。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;メッセージバスも実装方法の一つです👋。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;たとえば、タイヤの空気圧が変化すると、空気漏れを表すleakedドメインイベントを発行します。このイベントが「タイヤの空気が漏れた」と伝えることで、駆動系の変化やハンドルの感触の変化など、一連の反応が起きます。ドメインイベントには通常、すでに発生して取り消せない出来事だとわかるよう、過去形の名前を付けます。&lt;/p&gt;
&lt;h3 id="领域服务domain-service"&gt;ドメインサービス Domain Service&lt;/h3&gt;
&lt;p&gt;ドメイン内には、&lt;strong&gt;どのオブジェクトにも属さないように見える&lt;/strong&gt;操作があります。それはドメインの重要な振る舞いであり、無視したり、単純にいずれかのエンティティや値オブジェクトへ押し込んだりはできません。このような振る舞いを見つけたら、サービスとして宣言する方法が推奨されます。それがドメインサービスです。&lt;/p&gt;
&lt;p&gt;ドメインサービスは、マイクロサービスの「サービス」とは別の概念です。サービスと集約ルートは、どちらも複数のエンティティを操作できる点では似ていますが、考え方と役割が異なります。&lt;strong&gt;集約ルートはエンティティをまとめるものであり、サービスは複数のエンティティの状態を同期するものです。たとえば、itemエンティティをlistエンティティへ追加します&lt;/strong&gt;。mailをinboxへ配信する操作などが例です。そのため、Serviceを抽象化した名前は通常、動詞になります。&lt;/p&gt;
&lt;h2 id="ddd领域建模设计领域模型"&gt;DDDのドメインモデリング（ドメインモデルの設計）&lt;/h2&gt;
&lt;p&gt;DDDのドメインモデリングは、一般に次の手順で進めます：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;要件に基づいて、暫定的なサブドメインと境界づけられたコンテキスト、およびコンテキスト間の関係を決めます。&lt;/li&gt;
&lt;li&gt;各コンテキストの内部をさらに分析し、エンティティと値オブジェクトを見分けます。また、エンティティに失血、貧血、充血、膨張のどのコードモデルを使うかを決めます。&lt;/li&gt;
&lt;li&gt;エンティティと値オブジェクトを関連付けて集約し、集約の範囲と集約ルートを決めます。&lt;/li&gt;
&lt;li&gt;集約ルートのためのrepoを設計し、エンティティや値オブジェクトの生成方法を考えます。&lt;/li&gt;
&lt;li&gt;プロジェクトでドメインモデルを実装し、実践を通じて妥当性を検証します。不十分な点をモデルへフィードバックして、リファクタリングします。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;これが、最初に戦略的設計を行い、その後で戦術的設計を行う、DDDの二段階の設計です。&lt;/p&gt;
&lt;p&gt;実践では、エンティティに失血モデル（setter/getterだけを持つ）か、貧血モデル（属性の妥当性検証など、データベース操作を含まない簡単なロジックを持つ）を採用することを勧めます。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;実際には、これはDDDモデリングの方法の一つで、トップダウンの設計です。別の方法としてボトムアップもあり、先にエンティティや値オブジェクトなどのドメインモデルを定め、その後でサブドメインや境界づけられたコンテキストを決めます。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;（本節・完）&lt;/p&gt;
</description>
    <category>ソフトウェアアーキテクチャ</category><category>ドメイン駆動設計</category></item>
    <item>
      <title>『クリーンアーキテクチャ』再読（4）：コンポーネントの原則</title>
      <link>https://tommycheese.github.io/ja/blogs/%E5%86%8D%E8%AF%BB%E6%95%B4%E6%B4%81%E6%9E%B6%E6%9E%84%E4%B9%8B%E9%81%93%E5%9B%9B/</link>
      <pubDate>Sat, 06 Jul 2024 17:27:18 +0800</pubDate>
      <guid>https://tommycheese.github.io/ja/blogs/%E5%86%8D%E8%AF%BB%E6%95%B4%E6%B4%81%E6%9E%B6%E6%9E%84%E4%B9%8B%E9%81%93%E5%9B%9B/</guid>
      <description>&lt;h1 id="再读整洁架构之道四组件构建原则"&gt;『Clean Architecture』再読（四）コンポーネント構築の原則&lt;/h1&gt;
&lt;p&gt;第三回では、主にモジュールとクラスの設計を説明しました。今回はさらに一歩進み、コンポーネントをどのように設計するかを説明します。&lt;/p&gt;
&lt;p&gt;コンポーネントはソフトウェアのデプロイ単位であり、システム全体のデプロイにおいて独立してデプロイできる最小の実体です。コンポーネントは個別に開発でき、コンポーネント化されたプラグインアーキテクチャは、すでにごく一般的なソフトウェアの構築方法になっています。&lt;/p&gt;
&lt;h2 id="组件聚合"&gt;コンポーネントの凝集性&lt;/h2&gt;
&lt;p&gt;コンポーネントの凝集性は、どのモジュールやクラスをまとめて一つのコンポーネントにするべきかを示します。主に三つの原則があります：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;再利用・リリース等価の原則&lt;/li&gt;
&lt;li&gt;共通閉鎖の原則&lt;/li&gt;
&lt;li&gt;全再利用の原則&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="复用发布等同原则rep"&gt;再利用・リリース等価の原則 REP&lt;/h3&gt;
&lt;p&gt;REPによると、ソフトウェアの再利用の最小単位は、リリースの最小単位と等しくあるべきです。&lt;/p&gt;
&lt;p&gt;REPはコード再利用の観点から考える原則です。同じテーマと機能を持つコードをまとめてコンポーネントにし、ソフトウェアをコンポーネント単位で再利用することを勧めます。&lt;/p&gt;
&lt;p&gt;わかりやすく言うと、コンポーネントをパッケージ化してリリースすると、バージョン番号や一意な識別子が付きます。既存のライブラリを取り込んでコードを再利用する際、取り込む単位はコンポーネントのパッケージであり、指定する手がかりはバージョン番号です。&lt;/p&gt;
&lt;h3 id="共同闭包ccp"&gt;共通閉鎖の原則 CCP&lt;/h3&gt;
&lt;p&gt;CCPによると、同じ目的で同時に変更されるクラスを同じコンポーネントに置き、同じ目的では変更されず、同時にも変更されないクラスは別のコンポーネントに置くべきです。&lt;/p&gt;
&lt;p&gt;CCPはコード保守の観点から考える原則です。同じ理由や目的で変更されるコードをコンポーネントとしてまとめることで、リリース、検証、デプロイに伴う作業負担を効果的に減らせます。&lt;/p&gt;
&lt;p&gt;前述のとおり、CCPはSRPのコンポーネント版です。SRPとCCPは、どちらも次のようにまとめられます：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;同じ理由で変更され、同時に変更する必要がある「もの」をまとめる。異なる理由で変更され、同時には変更されない「もの」を分ける。&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SRPにおける「もの」は、関数や、クラスまたはモジュールを構成する他の要素です。&lt;/li&gt;
&lt;li&gt;CCPにおける「もの」は、コンポーネントを構成するクラスとモジュールです。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="共同复用crp"&gt;全再利用の原則 CRP&lt;/h3&gt;
&lt;p&gt;CRPによると、コンポーネントの利用者に、必要のないものへの依存を強いてはいけません。&lt;/p&gt;
&lt;p&gt;CRPは、利用場面や利用頻度が異なるコードを、別々のコンポーネントへ分けることを求めます。不要な分割を避けるための原則でもあり、インターフェース分離の原則ISPを一般化したものです。&lt;/p&gt;
&lt;p&gt;CRPはインターフェース分離を一般化した原則です。ISPとCRPは、どちらも次のようにまとめられます：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;使う必要がない「もの」に依存しない。&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ISPにおける「もの」は、不要なメソッドを含む関数／クラスです。&lt;/li&gt;
&lt;li&gt;CCPにおける「もの」は、不要な関数を含むクラス／モジュールです。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="思考"&gt;考えてみる&lt;/h3&gt;
&lt;p&gt;では、REP、CCP、CRPの三つには、どのような関係があるのでしょうか。&lt;/p&gt;
&lt;p&gt;実際には、この三つは&lt;strong&gt;競合する関係&lt;/strong&gt;にあります。&lt;/p&gt;
&lt;p&gt;REPとCCPはまとめる方向の原則であり、どのクラス／モジュールを同じコンポーネントに入れるべきかを示します。一方CRPは分ける方向の原則であり、どのクラス／モジュールを一緒にしてはいけないかを示します。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tommycheese.github.io/blogimages/image-20240706143525187.png" alt="image-20240706143525187"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;REPとCCPだけに注目すると、依存先のコンポーネントに不要な部分が多く含まれ、不要なリリースが増えてしまいます。&lt;/li&gt;
&lt;li&gt;CRPとCCPだけに注目すると、ソフトウェアの再利用が非常に難しくなります。&lt;/li&gt;
&lt;li&gt;REPとCRPだけに注目すると、一部のクラス／モジュールを変更する際、関連する多くのモジュールも変更せざるを得なくなります。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;アーキテクトの仕事は、これら三つの原則の間でバランスを取ることです。コンポーネントの構成は、プロジェクトの重点や、開発しやすさと再利用しやすさの重みに応じて進化するべきです。どの方向へ進むかは、状況に合わせて判断する必要があります。&lt;/p&gt;
&lt;h2 id="组件耦合"&gt;コンポーネントの結合&lt;/h2&gt;
&lt;p&gt;コンポーネントの結合は、コンポーネント同士の関係をどのように構成するかを示します。三つの原則があります：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;非循環依存の原則 ADP&lt;/li&gt;
&lt;li&gt;安定依存の原則 SDP&lt;/li&gt;
&lt;li&gt;安定度・抽象度の原則 SAP&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="无依赖环原则adp"&gt;非循環依存の原則 ADP&lt;/h3&gt;
&lt;p&gt;ADPは、コンポーネントの依存関係グラフに循環があってはならないと教えています。&lt;/p&gt;
&lt;p&gt;バージョン番号による管理は「翌朝症候群」を解決しますが、その仕組みはADPに従う必要があります。まず、著者が翌朝症候群をどう説明しているかを見ましょう：&lt;/p&gt;
&lt;p&gt;丸一日かけてようやくコードを動かせたのに、翌日出勤すると、なぜかまた動かなくなっていることがあります。たいていは、他の担当者が、プロジェクトの依存先のコンポーネントを変更したためです。&lt;/p&gt;
&lt;p&gt;この問題の解決には、一般に二つの方法があります：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;週次ビルド&lt;/li&gt;
&lt;li&gt;バージョン番号による管理&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;&lt;u&gt;週次ビルド&lt;/u&gt;&lt;/strong&gt;：各自が自分のリポジトリで作業し、毎週決まった時間、たとえば金曜日にプロジェクトをビルドして、発生した競合を解消します。&lt;/p&gt;
&lt;p&gt;この方法の限界は明らかです：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;プロジェクトが大きくなるほど、統合作業を時間内に終えるのが難しくなります。&lt;/li&gt;
&lt;li&gt;プロジェクト全体のビルドとテストが難しくなり、チームのフィードバックサイクルが長くなり、開発品質も下がっていきます。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;そのため、バージョン番号による管理を導入する必要があります。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;u&gt;バージョン番号による管理&lt;/u&gt;&lt;/strong&gt;：コンポーネントの新バージョンがリリースされるたびに、それに依存する各チームは、すぐに採用するかどうかを自分たちで判断できます。&lt;/p&gt;
&lt;p&gt;バージョン番号による管理では、&lt;strong&gt;コンポーネントの依存関係グラフに循環を許してはいけません&lt;/strong&gt;。つまりADPに従う必要があります。そうしなければ、翌朝症候群は避けられません。なぜでしょうか。&lt;/p&gt;
&lt;p&gt;循環依存があると、循環内のすべてのコンポーネントが一つの大きなコンポーネントのようになり、他のコンポーネントが正常に依存するためには、すべてが整合するバージョンを使う必要があるからです。循環依存があるシステムでは、テストも厄介になります。スタブやmockは広く使われていますが、循環内の各コンポーネントに繰り返しスタブを作るのは、洗練された方法とは言えません。&lt;/p&gt;
&lt;p&gt;では、循環依存をどう解消すればよいのでしょうか。二つの方法があります：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;依存性の逆転を使い、インターフェースを作る。&lt;/li&gt;
&lt;li&gt;新しいコンポーネントを作る。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;DIPによる循環依存の解消は理解しやすいので、ここでは仕事で出会った事例を使い、新しいコンポーネントを作って循環依存を解消する方法を説明します。&lt;/p&gt;
&lt;p&gt;チームがドメイン駆動設計でシステムを設計している場面を考えます。EntityのDogは、Saveによる保存のためにDog Repoに依存します。つまりEntityモジュールがRepoモジュールに依存しています。ところが残念なことに、Entityモジュール内にはDog POまで定義されています。困った設計です。RepoのSaveも、オブジェクトの変換と保存にPOを使う必要があるので、EntityとRepoの間に循環依存が生まれます。幸い、Goではモジュール間の相互依存が許されず、チェック時に「循環インポート」エラーが出ます。どう対処すればよいでしょうか。&lt;/p&gt;
&lt;p&gt;答えは、新しいPOモジュールを作り、EntityにあるPOを移すことです。Repo内でPOに依存する関数もPO側へ分ければ、依存の循環がなくなります。依存性注入を使う方法もあります。SprintBootなら@AutoWiredで解決できますが、その本質も依存関係を管理するプールという新しいコンポーネントを作り、循環を解消することです。&lt;/p&gt;
&lt;p&gt;似た例は数多くあります。anacondaでPythonライブラリをダウンロードするときに起きた、ライブラリのバージョン競合を覚えているでしょうか……。&lt;/p&gt;
&lt;p&gt;少し補足すると、私はコンポーネント構造を設計する際、自然にシステムの機能と対応させることを考えていました。コンポーネントとシステムの機能は一対一に対応し、依存関係図も機能モジュールの分割と対応するはずだと思ったのです。システム設計の最初にコンポーネントの依存関係図も出来上がるものだと、当然のように考えていました。&lt;/p&gt;
&lt;p&gt;しかし著者は、トップダウンの設計は不可能だと明言しています。本書の説明を見てみましょう：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]&lt;/p&gt;
&lt;p&gt;コンポーネントの構造図は、ソフトウェアシステムの変化に合わせて変化し、拡張される必要があり、構築の最初に完璧に設計することはできません。依存関係図は機能と一対一に対応するものではなく、むしろアプリケーションのビルドしやすさと保守しやすさの地図だからです。&lt;/p&gt;
&lt;p&gt;設計、実装されたモジュールが増えるにつれて、コンポーネントの依存関係を管理する必要が出てきます。変更の影響範囲をできるだけ小さくしたいので、単一責任の原則SRPと共通閉鎖の原則CCPを適用し、同時に変更されることが多いクラスをまとめます。&lt;/p&gt;
&lt;p&gt;コンポーネント構造図の重要な目標の一つは、頻繁な変更をどう隔離するかを示すことです。頻繁に変更されるコンポーネントが、本来安定しているべき他のコンポーネントへ影響することは避けたいのです。&lt;/p&gt;
&lt;p&gt;アプリケーションの成長に伴い、再利用可能なコンポーネントを作る必要性も高まります。そこでCRPがコンポーネントの構成に影響し始めます。さらに循環依存が現れると、非循環依存の原則ADPを適用することで、コンポーネントの依存関係は組み替えられ、拡張されます。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="稳定依赖原则sdp"&gt;安定依存の原則 SDP&lt;/h3&gt;
&lt;p&gt;SDPは、依存関係をより安定した方向へ向けるべきだと教えています。通常、ソフトウェアアーキテクチャの下層ほど安定させるようにします。&lt;/p&gt;
&lt;p&gt;頻繁に変更されると予想するコンポーネントに、変更しにくいコンポーネントが依存してはいけません。そうすると、変化の多いコンポーネントまで変更しにくくなってしまいます。これがソフトウェア開発の難しいところです。変更しやすいように丁寧に設計したコンポーネントでも、誰かが一本の依存関係を追加しただけで、非常に変更しにくくなり得ます。SDPに従えば、この問題を避けられます。&lt;/p&gt;
&lt;p&gt;コンポーネントの安定性は、指標を使って判断できます。著者は次の式で計算する指標を定義しています：
$$
I=\frac{Fan-out}{Fan-in+Fan-out}
$$
Fan-inはコンポーネントに入ってくる依存の数、Fan-outは外へ向かう依存の数です。外への依存が少ないほどコンポーネントは安定します。外への依存が0なら、コンポーネントを変更させる要因がないことを意味するからです。&lt;/p&gt;
&lt;p&gt;安定依存の原則SDPは、各コンポーネントのIが、依存先のコンポーネントのIより大きいことを求めます。つまり、矢印の先のコンポーネントのほうが安定している必要があります。&lt;/p&gt;
&lt;p&gt;SDPは、すべてのコンポーネントを安定させることを求めているわけではありません。コンポーネントの構造図を設計する目的は、どれを安定させ、どれを不安定にするかを決めることです。すべてのコンポーネントが安定しているアーキテクチャは柔軟ではなく、十分なアーキテクチャの価値を持ちません。&lt;/p&gt;
&lt;h3 id="稳定抽象原则sap"&gt;安定度・抽象度の原則 SAP&lt;/h3&gt;
&lt;p&gt;SAPによると、コンポーネントの抽象度は安定度に見合っているべきです。&lt;/p&gt;
&lt;p&gt;高レベルの方針などは安定したコンポーネントに置くべきですが、そのままでは変更しにくくなります。幸い、オープン・クローズドの原則OCPは、安定したコンポーネントでも拡張しやすく設計できることを教えています。抽象クラスには安定した抽象化の能力があり、安定性を保ちつつ、拡張や変更がしやすい設計にできます。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;抽象クラスは、インターフェースとクラスの間の緩衝地帯です。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;安定度・抽象度の原則SAPは、コンポーネントの安定度と抽象度を結び付けます。安定したコンポーネントは抽象的でもあるべきであり、そうすれば安定性が拡張性を損ないません。一方、不安定なコンポーネントには具象実装を含め、その不安定さを具体的なコードの変更によって扱いやすくします。したがって、安定したコンポーネントにしたいなら、将来の拡張に備えてインターフェースと抽象クラスで構成するべきです。&lt;/p&gt;
&lt;p&gt;安定度と同様に、コンポーネントの抽象度も指標で測れます：
$$
A=\frac{N_c}{N_a}
$$
Ncはコンポーネント内のクラスの数、Naは抽象クラスとインターフェースの数を指します。Aの範囲は0から1で、0は抽象クラスが一つもないこと、1は抽象クラスだけで構成されていることを意味します。&lt;/p&gt;
&lt;h3 id="思考-1"&gt;考えてみる🤔&lt;/h3&gt;
&lt;p&gt;SDPとSAPには、どのような関係があるのでしょうか。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;まず、SDP、SAP、DIPの関係から考えます&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;実際には、SDP＋SAP＝コンポーネントレベルのDIPです。SDPは依存関係をより安定した方向に向けることを求め、SAPは安定性そのものが抽象化を必要とすることを教えます。つまり、依存関係はより抽象的な方向へ向かうべきです。&lt;/p&gt;
&lt;p&gt;これはどう理解すればよいのでしょうか。&lt;/p&gt;
&lt;p&gt;DIPがクラスに対して果たす役割は、柔軟性の確保です。SDP＋SAPも、コンポーネントを安定させつつ十分な柔軟性を保つためのものです。どちらもアーキテクチャ上では、具象実装から抽象クラス／インターフェースへ依存を向けます。そのため、役割としてはSDP＋SAP＝コンポーネントレベルのDIPと考えられます。&lt;/p&gt;
&lt;p&gt;ただし、DIPとSDP＋SAPには違いもあります。クラスの設計には中間がなく、抽象クラスか、そうでないかのどちらかです。SDPとSAPはコンポーネントレベルに適用されるため、部分的に抽象的で、部分的に安定したコンポーネントも認める必要があります。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;次に、SAPとSDPが目指すものから考えます&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;SDPは安定したものへ依存を向けることを求め、SAPは高レベルの方針を抽象的なコンポーネントに置くことを求めます。これによって、アーキテクチャ設計で方針と詳細を分けやすくなり、プラグイン方式の開発もしやすくなります。SDPとSAPの目的は一致しています。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ここでもまた、&lt;strong&gt;プラグイン方式の開発&lt;/strong&gt;という言葉が出てきました。前回はどこで出てきたでしょうか。そう、依存性逆転の原則DIPです。このことも、SAP＋SDPがコンポーネントレベルのDIPに似ていることを示しています。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="主序列"&gt;主系列&lt;/h3&gt;
&lt;p&gt;コンポーネントの不安定度Iと抽象度Aを組み合わせて図にすると、a=-i+1という直線を主系列と呼びます。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tommycheese.github.io/blogimages/image-20240706171725797.png" alt="image-20240706171725797"&gt;&lt;/p&gt;
&lt;p&gt;図全体は三つの領域に分けられます：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;苦痛の領域：(0,0)に近い領域です。ここにあるコンポーネントは非常に安定していますが、同時に非常に具象的なので、変更しにくくなります。データベースのテーブルが例です。ユーティリティライブラリもこの領域のコンポーネントです。Iが1で、多くのコンポーネントに依存するため不安定であっても、変更すると多くのコードに問題が出るので変更できません。&lt;/li&gt;
&lt;li&gt;無用の領域：(1,1)に近い領域です。ここのコンポーネントは通常きわめて抽象的ですが、他のコンポーネントから依存されておらず、利用できないことが多くあります。この領域のソースコードやクラスの設計上の問題は、削除し忘れた古いコードなど、歴史的な理由で生じることが一般的です。&lt;/li&gt;
&lt;li&gt;主系列の領域：主系列全体に沿った領域です。コンポーネントにとって最適なのは、線の両端である(0, 1)と(1, 0)です。優れたアーキテクトは、自分が設計するコンポーネントの大部分を、できるだけこの二点へ近づけるべきです。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;コンポーネントを総合的に評価するにはどうすればよいでしょうか。答えは主系列からの距離を計算し、設計が主系列にどれほど適合しているかを数値化することです。この値をDと呼び、0なら主系列上、1なら主系列から最も遠い位置にあります。
$$
D=|A+I-1|
$$
Dが0からどれだけ離れているかを、コンポーネントのリファクタリングや再設計の指針にできます。&lt;/p&gt;
&lt;p&gt;Dにはほかにも用途があります。たとえば、設計内の全コンポーネントのDの平均と分散を計算し、統計的にシステム設計を分析できます：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;良いシステム設計では、Dの平均と分散はともに0に近いはずです。&lt;/li&gt;
&lt;li&gt;分散をコンポーネントの「合格基準」として利用し、設計の中で通常の範囲から外れたコンポーネントを見つけられます。&lt;/li&gt;
&lt;li&gt;Dの分散を時系列で追跡すれば、ソフトウェアアーキテクチャの安定性と抽象性が時間とともにどう変化するかを観察できます。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;（第四回・完）&lt;/p&gt;
</description>
    <category>ソフトウェアアーキテクチャ</category><category>クリーンアーキテクチャ</category></item>
    <item>
      <title>『クリーンアーキテクチャ』再読（3）：SOLID 設計原則</title>
      <link>https://tommycheese.github.io/ja/blogs/%E5%86%8D%E8%AF%BB%E6%95%B4%E6%B4%81%E6%9E%B6%E6%9E%84%E4%B9%8B%E9%81%93%E4%B8%89/</link>
      <pubDate>Fri, 05 Jul 2024 14:11:30 +0800</pubDate>
      <guid>https://tommycheese.github.io/ja/blogs/%E5%86%8D%E8%AF%BB%E6%95%B4%E6%B4%81%E6%9E%B6%E6%9E%84%E4%B9%8B%E9%81%93%E4%B8%89/</guid>
      <description>&lt;h1 id="再读整洁架构之道三solid原则"&gt;『Clean Architecture』再読（三）SOLID原則&lt;/h1&gt;
&lt;p&gt;ソフトウェアモジュールを構築する主な目標は三つあります：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;変更に耐えられるソフトウェアにすること&lt;/li&gt;
&lt;li&gt;ソフトウェアを理解しやすくすること&lt;/li&gt;
&lt;li&gt;複数のソフトウェアシステムで再利用できるコンポーネントを構築すること&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;SOLID原則は、データと関数をどのようにクラスへまとめ、それらのクラスをどのようにつなげてプログラムにするかを教えてくれます。SOLID原則はモジュール設計の指針であり、アーキテクチャ設計のレベルでは別の設計原則も使います。&lt;/p&gt;
&lt;p&gt;SOLID原則とは、単一責任の原則&lt;strong&gt;S&lt;/strong&gt;RP、オープン・クローズドの原則&lt;strong&gt;O&lt;/strong&gt;CP、リスコフの置換原則&lt;strong&gt;L&lt;/strong&gt;SP、インターフェース分離の原則&lt;strong&gt;I&lt;/strong&gt;SP、依存性逆転の原則&lt;strong&gt;D&lt;/strong&gt;IPを指します。&lt;/p&gt;
&lt;h2 id="单一职责原则srp"&gt;単一責任の原則 SRP&lt;/h2&gt;
&lt;p&gt;SRPは、各ソフトウェアモジュールが一つの機能に責任を持ち、&lt;strong&gt;モジュールを変更する理由は一つだけであるべき&lt;/strong&gt;だと定めます。どのソフトウェアモジュールも、ある一種類のアクターだけに責任を持つべきです。&lt;/p&gt;
&lt;p&gt;コンポーネントレベルのSRPは、共通閉鎖の原則CCPと呼ばれます。&lt;/p&gt;
&lt;p&gt;モジュールの設計でSRPに従わないと、何が起きるのでしょうか。例を見てみましょう。&lt;/p&gt;
&lt;p&gt;経理部門、人事部門、開発部門が、同じ給与・勤務時間計算プログラムに依存しているとします。プログラムには三つの関数があります：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CalculateSalary：給与を計算する。&lt;/li&gt;
&lt;li&gt;CalculateTime：勤務時間を計算する。&lt;/li&gt;
&lt;li&gt;Save：情報を保存する。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;三つの部門は問題なくプログラムを使っていました。ところがある日、経理部門の給与計算のルールが変わり、保守担当者がCalculateSalary関数を変更しました。すると奇妙なことに、人事部門や開発部門の給与も同時に変わってしまったのです……。&lt;/p&gt;
&lt;h2 id="开闭原则ocp"&gt;オープン・クローズドの原則 OCP&lt;/h2&gt;
&lt;p&gt;「追加を受け入れ、変更を避けよう！」&lt;/p&gt;
&lt;p&gt;OCPによると、ソフトウェアシステムを変更しやすくするには、既存のコードの変更に頼るだけではなく、新しいコードの追加によって振る舞いを変えられる設計にしなければなりません。適切に設計されたソフトウェアは、拡張しやすく、既存部分の変更を必要としにくいものです。&lt;/p&gt;
&lt;p&gt;OCPはクラスやモジュールの設計だけでなく、コンポーネントの設計にも適用できます。その起源はデバイス独立性の概念にあります。具体的には、依存性の逆転を使ってIOデバイスをプラグインとして設計すれば、既存のIOデバイスを変更せずに、新しいIOデバイスを簡単に追加できます。&lt;/p&gt;
&lt;p&gt;OCPを実現するには、システムを一連のコンポーネントに分け、それらの依存関係を階層的に構成します。こうすることで、低レベルのコンポーネントが変更されても、高レベルのコンポーネントは影響を受けません。&lt;/p&gt;
&lt;h2 id="里式替换lsp原则"&gt;リスコフの置換原則 LSP&lt;/h2&gt;
&lt;p&gt;LSPによると、置き換え可能なコンポーネントでソフトウェアシステムを構築したいなら、それらのコンポーネントは共通の契約に従い、互いに置き換えられなければなりません。&lt;/p&gt;
&lt;p&gt;LSPの本質は置換可能性の原則です。型Sの各オブジェクトo1に対し、型Tのオブジェクトo2が存在し、T型を操作するプログラムPでo2をo1に置き換えても振る舞いが変わらないなら、SをTのサブタイプと呼べます。&lt;/p&gt;
&lt;p&gt;LSPはソフトウェアアーキテクチャのレベルにも適用できますし、適用すべきです。置換可能性が破られると、それに対処するための複雑な仕組みをアーキテクチャに大量に追加しなければならなくなるからです。&lt;/p&gt;
&lt;h2 id="接口隔离原则isp"&gt;インターフェース分離の原則 ISP&lt;/h2&gt;
&lt;p&gt;ISPによると、設計では不要な依存を避けるべきです。どのレベルのソフトウェア設計でも、必要のないものに依存すると、予期しない問題につながります。&lt;/p&gt;
&lt;h2 id="依赖反转dip"&gt;依存性逆転の原則 DIP&lt;/h2&gt;
&lt;p&gt;DIPによると、高レベルの方針を表すコードは、低レベルの詳細を実装するコードに依存してはいけません。逆に、低レベルの詳細を実装するコードが、高レベルの方針を表すコードに依存すべきです。&lt;/p&gt;
&lt;p&gt;依存性の逆転を直感的に理解するには、制御の流れとソースコードの依存の流れを考えます。たとえば制御がコンポーネントAからBへ流れる場合、BはA側の呼び出しに応じた実装を提供し、AはBのモジュールを取り込む必要があるため、コードの依存もAからBへ向かいます。この状態では、システムの振る舞いが制御の流れを決め、制御の流れがソースコードの依存関係を決めるため、アーキテクチャには選択の余地がありません。しかしインターフェースを導入すると、Bはそのインターフェースを実装し、Aはインターフェースを取り込むだけでよくなり、Bのモジュールを直接取り込む必要がなくなります。こうして制御の流れとソースコードの依存の向きが逆転します。これが「依存性の逆転」です。&lt;/p&gt;
&lt;p&gt;依存性の逆転は、ソフトウェアアーキテクチャの設計に自由をもたらします。柔軟なシステムを設計するには、ソースコードレベルの依存関係で、具象実装よりも抽象型、たとえばインターフェースや抽象クラスを多く参照すべきです。&lt;/p&gt;
&lt;p&gt;DIPから、いくつかのコーディング指針を導けます：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;コードでは抽象インターフェースを多く利用し、変化しやすい具象実装クラスの利用をできる限り避ける。&lt;/li&gt;
&lt;li&gt;具象実装クラスから派生クラスを作らない。&lt;/li&gt;
&lt;li&gt;具象実装を持つ関数をオーバーライドしない。&lt;/li&gt;
&lt;li&gt;特定の具象実装に関係する名前や、変化しやすいものの名前をコードに書き込まない。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;（第三回・完）&lt;/p&gt;
</description>
    <category>ソフトウェアアーキテクチャ</category><category>クリーンアーキテクチャ</category></item>
    <item>
      <title>『クリーンアーキテクチャ』再読（2）：プログラミングパラダイム</title>
      <link>https://tommycheese.github.io/ja/blogs/%E5%86%8D%E8%AF%BB%E6%95%B4%E6%B4%81%E6%9E%B6%E6%9E%84%E4%B9%8B%E9%81%93%E4%BA%8C/</link>
      <pubDate>Fri, 05 Jul 2024 07:25:33 +0800</pubDate>
      <guid>https://tommycheese.github.io/ja/blogs/%E5%86%8D%E8%AF%BB%E6%95%B4%E6%B4%81%E6%9E%B6%E6%9E%84%E4%B9%8B%E9%81%93%E4%BA%8C/</guid>
      <description>&lt;h1 id="再读整洁架构之道二"&gt;『Clean Architecture』再読（二）&lt;/h1&gt;
&lt;p&gt;プログラミングパラダイムとはプログラムの書き方の型であり、どのようなときにどのコード構造を採用すべきかを示すものです。&lt;/p&gt;
&lt;p&gt;これまでに登場したプログラミングパラダイムは、構造化プログラミング、オブジェクト指向プログラミング、関数型プログラミングの三つです。&lt;/p&gt;
&lt;p&gt;著者によると、それぞれのパラダイムはアーキテクトの武器を増やすものではありません。アーキテクトやプログラマーはすでに十分な武器を持っており、この三つのパラダイムは、その武器の使い方を&lt;strong&gt;制限&lt;/strong&gt;するものです。だからこそ「パラダイム」と呼ばれます。&lt;/p&gt;
&lt;h2 id="结构化编程范式"&gt;構造化プログラミング&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;構造化プログラミングは、プログラムの制御の直接的な移動を制限し、規律を与えます。特に、プログラム内でgotoを自由に使うことを制限します。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="分解程序"&gt;プログラムの分解&lt;/h3&gt;
&lt;p&gt;Dijkstraは数学的な導出によってプログラムを推論し、証明することを目指しました。プログラムをユークリッド的な構造にすることで、証明済みの構造をつなぎ合わせて新しいプログラムを作り、プログラム全体の正しさを導けると考えたのです。&lt;/p&gt;
&lt;p&gt;また、プログラムにgotoが多いと分解が難しくなる一方、goto文の役割は分岐と反復で十分に果たせることにも気づきました。&lt;/p&gt;
&lt;p&gt;このように構造化プログラミングは、プログラムを細かく分解できることを示しています。大きな問題を一連の高レベル関数の組み合わせに分解し、それぞれをさらに低レベルの関数へ分解することを、再帰的に続けられます。さらに、分解して得られた各関数も構造化プログラミングで記述できます。&lt;/p&gt;
&lt;p&gt;しかし、このような形式的証明に基づくプログラミングは主流にはなりませんでした。現在は、科学的方法による検証がより広く使われています。&lt;/p&gt;
&lt;h3 id="科学证明法"&gt;科学的方法による検証&lt;/h3&gt;
&lt;p&gt;科学理論や科学法則は反証できますが、証明することはできません。同様に、プログラムもテストによって反証できるだけで、正しさを証明することはできません。つまり「テストはバグの存在を示せるだけで、バグがないことは証明できない」のです。科学的方法を使うと、構造化プログラミングは、まずプログラムを証明可能な小さな関数へ再帰的に分解し、それから関連する&lt;strong&gt;テスト&lt;/strong&gt;を書いて、それらの関数が誤っていることを示そうとするよう促します。テストで反証できなければ、関数は十分に正しいとみなし、プログラム全体も正しいと推論できます。&lt;/p&gt;
&lt;h2 id="面向对象编程范式"&gt;オブジェクト指向プログラミング&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;オブジェクト指向プログラミングは、プログラムの制御の間接的な移動を制限し、規律を与えます。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;具体的には、オブジェクト指向プログラミングは&lt;strong&gt;ポリモーフィズム&lt;/strong&gt;によって関数ポインタの利用を制限します。関数ポインタが指せる対象に制限を設けることだと考えられます。たとえばjavaのポリモーフィズムは、一般に継承関係にある二つのクラスのオブジェクト間で使われます。&lt;/p&gt;
&lt;p&gt;著者はポリモーフィズムをOOPの最大の特徴と考えています。ポリモーフィズムを使えば、&lt;strong&gt;依存性の逆転&lt;/strong&gt;を実現できます：&lt;/p&gt;
&lt;p&gt;依存性の逆転とは、インターフェースを導入することで、システムの制御の流れに縛られず、すべてのソースコードの依存関係を完全に制御することです。&lt;/p&gt;
&lt;p&gt;依存性の逆転によって、システムをプラグイン方式で開発できます。たとえばweb UIやデータベースと業務ロジックの依存関係を逆転させれば、中核となる業務ロジックをUIやデータベースから切り離せます。こうして、UIやデータベースは業務ロジックのプラグインになります。&lt;/p&gt;
&lt;p&gt;オブジェクト指向プログラミングとは、ポリモーフィズムを使ってソースコードの依存関係を制御する能力です。この能力により、アーキテクトは高レベルの方針を担うコンポーネントと低レベルの実装を担うコンポーネントを分離したプラグインアーキテクチャを構築できます。低レベルのコンポーネントはプラグインとしてコンパイルでき、高レベルのコンポーネントとは独立して開発、デプロイできます。&lt;/p&gt;
&lt;h2 id="函数式编程范式"&gt;関数型プログラミング&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;関数型プログラミングは、プログラム内の代入を制限し、規律を与えます。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;言い換えると、関数型プログラミング言語の変数は不変です。&lt;/p&gt;
&lt;p&gt;著者は次のように述べています。以下は原文からの引用です：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;「すべての競合状態、デッドロック、並行更新の問題は、可変変数によって生じる。変数が一切変更されなければ、競合状態や並行更新の問題は起こり得ない。ロックの状態が不変であれば、デッドロックの問題も起こり得ない。」&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;記憶装置やプロセッサの速度の制約を考えなければ、不変性は実現できます。しかし実際にはこれらを考慮する必要があるため、不変性を適用できる範囲には限界があります。不変性を現実的に活用するためには、&lt;strong&gt;可変性の分離&lt;/strong&gt;が必要です。&lt;/p&gt;
&lt;h3 id="可变性的隔离"&gt;可変性の分離&lt;/h3&gt;
&lt;p&gt;可変性を分離する一般的な方法の一つは、アプリケーションやその内部サービスを分割して、&lt;strong&gt;可変コンポーネントと不変コンポーネントに分けること&lt;/strong&gt;です。不変コンポーネントは純粋関数を使って処理を行い、その間に状態を変更しません。変数の状態を変更する必要があるときは、一つ以上の非関数型コンポーネント、つまり可変コンポーネントと通信します。&lt;/p&gt;
&lt;p&gt;例としてGITバージョン管理ツールがあります。ポインタを移動することでファイルの追加、削除、変更を記録しますが、実際にファイルが変更されたり削除されたりするわけではありません。あるのは追加と検索だけです。これも不変性の一例ではないでしょうか。&lt;/p&gt;
&lt;p&gt;mysqlのトランザクション管理やトランザクショナルメモリの動作にも、この考え方が使われています。&lt;/p&gt;
&lt;h2 id="总结"&gt;まとめ&lt;/h2&gt;
&lt;p&gt;三つのプログラミングパラダイムは、ソフトウェアアーキテクチャと密接に関係しています：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ポリモーフィズムは、境界を越えるための手段です。&lt;/li&gt;
&lt;li&gt;関数型プログラミングは、データの保存場所とアクセス権を規定し、制限するための手段です。&lt;/li&gt;
&lt;li&gt;構造化プログラミングは、各モジュールの実装の基礎です。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;三つのパラダイムは、ソフトウェアアーキテクチャの三つの主要な関心事と一致します。すなわち、&lt;strong&gt;コンポーネントの独立性&lt;/strong&gt;（オブジェクト指向）、&lt;strong&gt;データ管理&lt;/strong&gt;（関数型）、そして機能（構造化）です。&lt;/p&gt;
&lt;h2 id="再次思考"&gt;改めて考える&lt;/h2&gt;
&lt;p&gt;三つのプログラミングパラダイムはいずれも、プログラマーに新たな&lt;strong&gt;制限&lt;/strong&gt;を課します。それぞれが特定のコードの書き方を制約しており、新しい能力を追加するものではありません。つまり、パラダイムが教えてくれるのは「何をしてはいけないか」です。&lt;/p&gt;
&lt;p&gt;（第二回・完）&lt;/p&gt;
</description>
    <category>ソフトウェアアーキテクチャ</category><category>クリーンアーキテクチャ</category></item>
    <item>
      <title>『クリーンアーキテクチャ』再読（1）</title>
      <link>https://tommycheese.github.io/ja/blogs/%E5%86%8D%E8%AF%BB%E6%95%B4%E6%B4%81%E6%9E%B6%E6%9E%84%E4%B9%8B%E9%81%93%E4%B8%80/</link>
      <pubDate>Wed, 03 Jul 2024 23:10:20 +0800</pubDate>
      <guid>https://tommycheese.github.io/ja/blogs/%E5%86%8D%E8%AF%BB%E6%95%B4%E6%B4%81%E6%9E%B6%E6%9E%84%E4%B9%8B%E9%81%93%E4%B8%80/</guid>
      <description>&lt;h2 id="前记"&gt;はじめに&lt;/h2&gt;
&lt;p&gt;なぜ「再読」なのでしょうか。以前にも『Clean Architecture』を一度読みましたが、その頃は実際のプロジェクトで経験を積み、試行錯誤する機会が少なく、理解が浅いと感じていました。その後、仕事でいくつかのプロジェクトや要件に取り組む機会に恵まれました。さらに幸運なことに、チームが採用していたのはクリーンアーキテクチャで、詳しく言えばイベント駆動開発のDDDとクリーンアーキテクチャを組み合わせたものでした。DDDの詳細は後で取り上げます。そこで本書📚を読み直したところ、多くの学びがありました。このシリーズでは、私なりの理解でクリーンアーキテクチャについて説明します。より正確には読書ノートとして、同じ分野に携わる方々と共有したいと思います。&lt;/p&gt;
&lt;p&gt;本書の詳細はこちらです：&lt;a href="https://weread.qq.com/web/bookDetail/480322f072021a3248038c8"&gt;『Clean Architecture』—ロバート・C・マーティン—微信読書（qq.com）&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;このシリーズでは本書の章立てに沿い、著者とともに理解を深めていきます。ただし再読なので、後の章の内容に触れることもあります。読んでいて理解しづらい内容や初めて聞く言葉が出てきたら、おそらく後の章で説明されるものです。&lt;/p&gt;
&lt;p&gt;それでは始めましょう。&lt;/p&gt;
&lt;h2 id="设计与架构的含义"&gt;設計とアーキテクチャの意味&lt;/h2&gt;
&lt;p&gt;設計とアーキテクチャに本質的な違いはありません。低レベルの設計の詳細と高レベルのアーキテクチャの情報が、一体となってソフトウェアシステムを定義します。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ソフトウェアアーキテクチャの究極の目標は、システムの構築と保守の要件を最小限の人的コストで満たすことです&lt;/strong&gt;。したがって、コストはソフトウェアアーキテクチャの設計の良し悪しを評価する指標になります。&lt;/p&gt;
&lt;p&gt;設計を十分に行わず急いで構築したシステムは、&lt;strong&gt;混沌としたシステム&lt;/strong&gt;になりかねません。このようなシステムでは、構築の過程でコード品質や設計構造の改善が長期間にわたって軽視されています。アーキテクチャを丁寧に設計することで、システムが混沌に陥ることをある程度防ぎ、コストを抑えられます。&lt;/p&gt;
&lt;p&gt;ソフトウェア開発には、次の二つの重要な特徴があります：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;速く進むには、まず安定して進むこと。&lt;/li&gt;
&lt;li&gt;過信すると、設計のやり直しでも元のプロジェクトと同じ行き詰まりに陥ること。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="两个价值维度"&gt;二つの価値の側面&lt;/h2&gt;
&lt;p&gt;ソフトウェアシステムには、二種類の価値があります：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;振る舞いの価値：指定された方法で機械を動作させ、システムの利用者に利益をもたらす、または利益を増やすこと。&lt;/li&gt;
&lt;li&gt;アーキテクチャの価値：ソフトウェアが十分に柔軟であり、変更のコストが要件の範囲に依存し、その形には依存しないこと。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;要件の「範囲」と「形」は、どう理解すればよいのでしょうか。
私の理解では、範囲とは要件のおおまかな対象範囲と、それが属する&lt;strong&gt;ドメイン&lt;/strong&gt;を指し、形とは要件の具体的な内容を指します。&lt;/p&gt;
&lt;p&gt;では、どちらの価値がより重要なのでしょうか。
著者は、次の理由からアーキテクチャの価値のほうが重要だとしています：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;正常に動作していても変更できないプログラムは、要件が変わると正常に動作しなくなります。変更して再び動かすこともできないため、その価値は0になります。&lt;/li&gt;
&lt;li&gt;現在は正常に動作しなくても、簡単に変更できるプログラムなら、修正することも、要件の変化に合わせて継続的に変更することも容易なはずです。したがって、そのプログラムは価値を生み続けます。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;事業部門と開発部門が共通して犯しがちな誤りは、本当に緊急かつ重要な機能と、緊急ではあっても重要ではない機能を区別しないことです。その結果、重要なシステムアーキテクチャの問題が、重要ではない振る舞いの機能に道を譲ってしまいます。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;システムアーキテクチャの重要性と機能の緊急性を調整することは、ソフトウェア開発者自身の責任です&lt;/strong&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;筆者注：開発者同士でも、チームが異なれば似た問題が起こります。たとえば、バックエンドは一連のAPIを提供するだけなので簡単だと考え、適切な設計の重要性を理解していないフロントエンド部門もあります。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="为好的软件架构持续斗争"&gt;良いソフトウェアアーキテクチャのために戦い続けよう✊&lt;/h3&gt;
&lt;p&gt;ソフトウェアアーキテクチャの価値を軽視すると、システムは次第に保守が難しくなり、やがて変更できなくなります。そうなったなら、開発チームは要求を出す側と十分に議論せず、果たすべき責任を果たさなかったということです。&lt;/p&gt;
</description>
    <category>ソフトウェアアーキテクチャ</category><category>クリーンアーキテクチャ</category></item>
    </channel>
</rss>
