[和訳]チープなコードは書かず、コードには金をかけないこと

Kevin Mas Ruiz    
Kevin Mas Ruiz氏は、バルセロナに拠点を置くドメイン駆動開発者であり、vlingo.ioのコントリビュータも努めています。
この記事は、2019年12月に公開された記事の翻訳転載です。著者の許可を得て配信しています。
Do not write cheap code, make code cheap

この記事の中でスクリプトを例示する場合は Javaを使いますが、示されるパターンは極めて基本的なものであり、そのスクリプト例はJava以外にも、基本的なオブジェクト指向で機能的なサポートがあるほとんどの言語で使用することができます。​

優れたコードを白紙から記述することは、経験豊富な開発者にとっても困難な作業です。既存のコードベースを、理解しやすく、変更しやすく、テストが容易で、観察ができて、そしてプロジェクトの有効期間を通じてコードの過度な重複が発生しない方法で既存のコードベースを進化させることは、多くの開発者やチームが手掛けるべく必要な任務ですが、これが成功するのは極めて稀な例です。

モノリスは大きな泥団子になるべくして生まれてはいません。それは通常、テストが可能であり、それに取り組む数少ない開発者に進化し得る、そしてビジネスニーズを解決するのに効果的な、それなりのレベルにあるアーキテクチャを備えています。

私はこの記事のタイトルにした句文について何度か聞かれたことがあります。時間を超えて常に安価なコード作成する方法は? などと。変更しやすい優れたコードの書き方についてのハンドブックはありませんが、安価なコードの特性が見つかったので、彼らとはそれを共有したいと思います。

まず、コードを変更するについて、それが難しい理由をその事例と理論を以て分析させて頂きます。

共有インフラストラクチャコードを変更する難しさ

共有コードはコミットメントです。さまざまな機能を実際のロジックを実行する単一のホットスポットに向けて流し込む必要があります。これらのパターンの良い例は、基底クラスと効用関数です。

これにより、AbstractDAO の変更が困難になります。何故ならばその変更は、おそらくは無関係な3つのドメインに関わって、それが複数の機能に影響を及ぼすからです。

この問題を回避するには、次の 3 つの方法があります。

・共有コードを個別フレームワークに委譲します。
・インフラストラクチャコードをコードベースから切り離し、上部にカスタムフレームワークを構築します。
・コードを複製します。

最も安価なのは、個別フレームワークに委任する最初のオプションです。個別フレームワークは、既に最も一般的な問題の解決策を提供しています。可能であれば、インフラストラクチャも個別フレームワークに委譲するのが良いでしょう。

次に安価なオプションは、コードの複製です。複製コードが比較的に複雑でややこしい場合でも、それが安価であれば、複数の場所でまったく同じコードを持つ良い機会です。早期コードの再利用により、ソリューションの早期汎化の確率が高くなり、ビジネスに重点を置かないコードが生まれます。ビジネス言語を話さないコードは、ビジネス目的には使いにくく、またビジネス用に変更するのが困難です。

また、コードの複製は、サービスの可用性に役立ちます。ホットスポットを回避すると、ホットスポットに依存するすべてのクラス (メモリ リーク、接続プール リーク、および関連) に影響を与える一連の障害が発生する可能性が低くなります。

共有ドメインコードを変更する難しさ

最初のポイントに関連して、ドメイン コードも変更するのが困難です。

プロジェクトの開始時は、すべてが CRUD であるように見えます。操作に必要なのは、テキス ボックス、ドロップダウンボタンおよび送信ボタンのみです。その後には、送信されたアイテムの一覧と、その詳細をも確認することができます。

注意。ビジネスルールは時間を経るに従って複雑さを増し、今日の時点ではCRUDであるように見えたのが、おそらく数ヶ月後、それはまったく異なったものになります。それは多分、単にボタンが変わるだけであり、すべてがオートコンプリートになったり、異なるサードパーティのサービスから異なる情報を入力する複雑なユーザー体験になったりします。

ドメイン コードの共有を避け、エンティティの省略を避け、値オブジェクトの構成を優先してください。その一例として以下のJavaクラスをご覧ください。

public abstract class BaseBook {
    private long bookId;
    private String bookTitle;
    private String authorName;
    private BigDecimal price;
    private BigDecimal salesPrice;

    ...constructor
}

public class Book extends BaseBook {
    private long stock;
    private long warehouse;
}

BaseBook を拡張してロジックとプロパティを共有しすることは、例えそれが安全に見える場合であっても、基本クラスが大きくなると、Book の拡張が難しくなり、BaseBook の変更が難しくなります。また、プロパティの境界とプロパティ間の関係が不明確になります。むしろ境界を明示的にするためには、値オブジェクトを優先します。

public final class Cover { 
    private final String bookTitle;
    private final String bookName;

    ...constructor
}

public final class Author {
    private final String authorName;

    ...constructor
}

public final class Price {
    private final BigDecimal original;
    private final BigDecimal sales;

    ...constructor
}

public final class Stock {
    private final long quantity;
    private final long warehouse;

    ...constructor
}

public class Book {
    private final long bookId;
    private Cover cover;
    private Author author;
    private Price price;
    private Stock stock;

    ...constructor
}

値オブジェクトに関するナレッジを分割すると、コードは長期的に理解しやすくなり、そして値オブジェクトの変更は基本クラスの変更よりも自己完結型であるため、より容易に理解できます。

ファンイン(fan-in)関係の回避

クラス間のファンイン関係とは、クラスが別のクラス数からの直接的または間接的な依存関係である場合を言います。たとえば、同じドメインオブジェクトを使用する複数の機能を持つ場合などです。下図にその例を示します。

この場合は、次の予備的質問を自問自答で確認する必要があります。

・書籍や、これら行動のデータは必要だろうか?
これに対する答えがデータのみは必要とする場合は、(エンティティ全体ではなく) 値オブジェクトのみを使用するか、新しい読み取りモデルを定義することを検討してください。SQL データベースがある場合は、ビュー結果を共有するだけで済みます。単一ビューの移行は、コード内で 10 ポイントを超えるポイントで使用して行われるクラスの変更よりもはるかに簡単です。

・すべての場所における同様のデータまたは書籍に関する行動データは必要だろうか?
答えが「No」の場合は、新しい読み取りモデルを定義する必要があります。データと動作の両方が異なる場合は、おそらく異なる境界付けられた無関係なコンテキスト/製品を意味します。

・コードは、読んでいるだけの場合なのか?それとも書き込んでいる場合なのか?
書き込みを行っていて、動作が異なる場合は、おそらく異なる境界付きコンテキスト/製品にあります。この場合、コードを別々に保管します。読み取りのみを行う場合は、読み取りモデルで十分です。

ただし、これらとは異なった方法で、コード境界を明確にし、抽出および置換することを容易にすべく使用が可能なツールがいくつかあります。

製品に基づく線形階層を優先

並列階層 (XController、XService、X、X リポジトリ) は、データ (ユーザー コントローラー、ユーザー サービス、ユーザー、ユーザー リポジトリ) に基づいて定義され、依って他のサービス間で共有される傾向にあることから、その並列階層 (XController、XService、X リポジトリ) を変更することは困難です。これらの関係を定義することは避け、製品ベースの階層を定義する必要があります。

OneClickPurchase, StandardPurchase, SearchPage, Newsletterは、ビジネスレベルの表現力がより豊かであり、境界がより明確です。

値オブジェクト構成に基づくフラット階層を優先

接続は間違っていなくとも、正当な理由付けなく行うと、ホットスポットでの接続が遅れる可能性があります。値オブジェクトの構成を優先すると、そのコードのリファクタリングと抽出が容易になり、要約クラスにの場合に比べ、値オブジェクトのテストが比較的簡単になります。

フィーチャトグルを使用して境界を定義する

機能の切り替えを使用して境界を定義するコードを無効にできる場合は、コードを抽出できます。いつでも無効にできる機能切り替えを使用して境界を定義します。これにより、コードの信頼性が向上し、インシデントが発生した場合にいつでも製品や機能を無効にすることができます。機能の切り替えの使用例を以下に示します。

public final class StandardPurchase {
    private final StandardPurchaseContext context;
    private final DeliveryInformation delivery;
    private final FeatureToggle toggle;

    ...constructor

    public PurchaseSummary summary(User forUser) {
        PurchaseSummary summary = context.summary(forUser);

        if (toggle.enabled(ONE_DAY_DELIVERY)) {
            OneDayDeliveryForecast forecast = delivery.oneDayDelivery(forUser);
            return summary.oneDayDelivery(forecast);
        }

        return summary;
    }
}

テスト中に可能な場合はモックを再検討する

既定で依存関係をモックすると、テスト時間が短縮されます。ただし、外部依存関係のモックは比較的お粗末なので、コード内の境界がぼやける可能性もあります。

統合テストにコストがかかる場合は、クラスの大きなチャンクに依存し、将来テストするコードを簡単に抽出または変更できない可能性があります。


コメントを読む

新着ピック  






















新着ニュース

ソニー、6月5日の「PS5」イベントを延期 全米拡大の人種差別抗議運動への配慮で

「コストパフォーマンスを極めたい」 Xiaomiに聞く「Mi Note 10 Lite」「Redmi Note 9S」の狙い

CeleronやPentium Gまで——第10世代Core iシリーズ14モデルが店頭に並ぶ

“密”になるほどの人気で売り上げ絶好調 「ホームセンター」はコロナを機に復権できるか?

Travis CIでRepositoryのアクセス権ごとにどんな操作が可能であるか確認してみた | Developers.IO

S3のユーザーポリシーを使用したバケットへのアクセスコントロールを公式チュートリアルで理解する | Developers.IO

ポケモンGOで初の大規模メンテナンス 2日未明から7時間

NeovimでScalaの編集環境を整えてみた | Developers.IO

営業推進室にジョインしました大森です | Developers.IO

AWS事業本部コンサルティング部にジョインしました大村です | Developers.IO

トレンドマイクロの製品に脆弱性? “マイクロソフトからBAN”の疑惑 公式は「事実と異なる」

「iPhone SE(第2世代)」用ケース選び 背面手帳型ケースおすすめ3選【2020年最新版】

Amazonギフト券がAmazon以外でも使える Amazon Payから残高を利用可能に

AWS事業本部にジョインしました川上です! | Developers.IO

DA事業本部にジョインした石川葉月です | Developers.IO

Disney+(ディズニープラス)、6月11日に日本上陸 ドコモが国内独占で提供

ImageBuilder をサポートした CloudFormation で AMI作成から EC2のオートスケール起動まで実装してみた | Developers.IO

新卒社員が入社2ヶ月でAWS Cloud Practitionerを取得した話。 | Developers.IO

“株式市場”のように商品を売買するECサイト「StockX」日本上陸 スニーカーやアパレルに特化

身内に潜む敵? 実はリモートより怖いlocal攻撃

もっと見る
記事をPICKする
会員登録
Register
記事をPICKする

会員登録すると、もっと便利に利用できます。