前置き
前回の記事までに、いろいろとパイプラインアーキテクチャにおける設計原則などを
触れてきました。
実は、ETLなどのデータパイプラインアーキテクチャは、CI/CDパイプラインと同様に、
「ドメイン駆動設計(DDD)の戦略」と「オブジェクト指向(OOP)の設計原則」 を適用するための完璧なユースケースです。
CI/CDパイプラインが「コード」を処理するのに対し、
データパイプラインは「データ」を加工処理するだけで、アーキテクチャとして直面する課題(複雑さ、変更容易性、信頼性)は両者まったく同じだからです。
1. データパイプラインへのDDDの適用(戦略的設計)
下記の「2軸マトリクス」の考え方は、データパイプラインのプロセスを分類し、
「設計努力をどこに集中すべきか?」を判断するのに直結します。
象限1: コアパイプライン (高複雑・高変更)
会社の競争優位性に直結する、
独自の 「ビジネスロジックによるデータ変換(Transform)」 部分。
事例
独自の不正検知スコアの計算、需要予測モデルのロジック、複雑な名寄せ処理。
適用
ここにOOP原則を最大限に適用し、OCP(拡張性)とSRP(単一責任) を徹底します。
象限2: 汎用プラットフォーム (高複雑・低変更)
安定稼働が求められる、複雑な 「抽出(Extract)」や「ロード(Load)」 のフレームワーク部分。
事例
数テラバイトのデータをS3からDWHへ高速に並列ロードする共通コンポーネント。
適用
カプセル化を徹底し、この複雑さを隠蔽した「ブラックボックス」にします。
この時に、情報隠ぺいとコンポーネントの再利用観点での凝集原則もセットで用いることになります。
象限3: 支援プロセス (低複雑・高変更)
ビジネスルールの変更に伴い、「属性値」 が頻繁に変わる部分。
事例
「この閾値以下のデータは除外する」というフィルタリング処理。
適用
カプセル化(属性と振る舞いの分離) を徹底し、閾値などの「属性」をパイプライン定義(YAMLなど)から注入できるようにします。
象限4: 雑務プロセス (低複雑・低変更)
単純なデータの移動やリネーム。
適用
YAGNIを適用し、過剰な設計を避けます。ここへの投資は避けましょう。
2. データパイプラインへのOOPの適用(戦術的設計)
これらの戦略に基づき、具体的なOOP原則を適用します。
単一責任の原則 (SRP)
アンチパターン
E・T・Lをすべて詰め込んだ、数千行のモノリシックなPythonスクリプト。
リファクタリング
Extract
ジョブ、Transform-A
ジョブ、Transform-B
ジョブ、Load
ジョブ、と責務ごとに
プロセス(コンポーネント)を明確に分割します。
オープンクローズド原則 (OCP)
「新しいデータ品質チェックを1つ追加したい」という課題があったとします。
アンチパターン
モノリシックなTransform
スクリプトを 「修正」 する。
リファクタリング
既存のTransform
ジョブの「後続」に、新しいQualityCheck
ジョブを「拡張」として追加します。
これはイベント駆動の考え方(前工程の成功イベントをトリガーに動く)そのものです。
カプセル化 (属性と振る舞いの分離)
「不正検知の閾値を0.8から0.75に変更したい」という課題があったとします。
アンチパターン
スクリプト内の if score > 0.8:
という部分をハードコードで修正する。
リファクタリング
閾値を 「属性(パラメータ)」 としてTransformジョブの外部から注入(FRAUD_THRESHOLD=0.75
)し、スクリプト(振る舞い)は一切変更しないようにします。
結論
データパイプラインは、リファクタリングとかせずに放置すれば、アプリケーション同様に、すぐに 「ETLモノリス」 という、メンテナンス不可能な技術的負債と化します。
CI/CDパイプラインと同様に、DDDの戦略的設計で「コアドメイン」を特定し、濃淡をつけて、OOPの戦術的設計(SRP, OCP, カプセル化)を適用することこそが、データパイプラインをスケーラブルで変更容易、かつ信頼できるものにし続けるための必須条件です。
もちろん、不要なものを構築しないというYAGNIも必要です。
補足 CI/CDへのコンポーネント凝集原則の適応
CI/CDパイプラインを構成するスクリプトやジョブ群も、アプリケーションのコンポーネントとまったく同様に、凝集性に関する設計原則に従ってコンポーネント化することで、保守性が劇的に向上します。
🛡️ CCP (Common Closure Principle / 閉鎖性共通の原則)
「毎回セットで変更されるスクリプトは、同じモジュールと見なしてグルーピングする」
これは保守性の核心です。
例えば、「アプリケーションのビルド(build.sh)」と「そのアプリ固有のユニットテスト(run-tests.sh)」は、ビジネスロジックが変更されるたびに必ずセットで変更・実行されます。
リファクタリング
この2つのスクリプト(あるいはジョブ定義)を、build-and-test-component.yml のような単一の「パイプライン・コンポーネント」としてグルーピングします。
保守性の向上 (なぜ良いか?)
・変更の局所化
将来、ビルド方法の変更でテストの実行方法も変える必要が出た場合、修正するファイルはこの1つのコンポーネントだけで済みます。
・OCPへの準拠
関連しない他のパイプライン(例: security-scan)は、この変更の影響を一切受けません。
⚙️ REP & CRP (Reuse-Release Equivalence / Common Reuse Principles)
「あまり変更が入らない安定した適応度関数であり、毎回一緒に実行されるようなスクリプトは、グルーピングしておく」
これは再利用性と安定性の核心です。
例えば、「全社共通のセキュリティスキャン」や「標準的なDockerイメージビルド」は、
変更頻度が低く、多くのサービスで共通して再利用されます。
リファクタリング (REP)
これらの安定したスクリプト群を、platform-security-component:v1.2.0 のように、**バージョン管理された単一の「リリース可能コンポーネント」**としてグルーピングします。
リファクタリング (CRP)
このコンポーネントには、「セキュリティスキャン」と「ライセンススキャン」だけを入れ、「カオス実験」のような無関係なスクリプトを混ぜないようにします。
保守性の向上 (なぜ良いか?)
・安定性の担保
アプリケーション開発チームは、v1.2.0
という安定版のコンポーネントに安心して依存できます。
プラットフォームチームが v1.3.0
をリリースしても、既存のパイプラインは壊れません(REP)。
・不要な依存の排除
セキュリティスキャンだけが必要なチームが、カオス実験のスクリプトにまで依存しなくて済みます(CRP)。
原則間の「トレードオフ」とDDD
CI/CDパイプラインにおいても、これらの凝集性原則は トレードオフ(緊張関係) にあります。
CCPは、「変更」を軸にグルーピングするため、コンポーネントは大きくなりがちです。(例: order-service固有のビルド、テスト、適応度関数を全部まとめる)
CRPは、「再利用」を軸にグルーピングするため、コンポーネントは小さくなりがちです。(例: 汎用的なjava-build、汎用的なsecurity-scan)
ここで、DDDの2軸マトリクス(変更頻度 vs 複雑さ) が、このトレードオフを解決する指針となります。
象限1 (コアドメインのパイプライン)
CCPを優先します。
変更頻度が高く複雑なため、再利用性よりも「関連する変更を1箇所にまとめる」ことのメリットが上回ります。
象限2 & 4 (汎用/雑務のパイプライン)
REP/CRPを優先します。
変更頻度が低いため、安定した「再利用可能なコンポーネント」として切り出し、プラットフォーム化するメリットが上回ります。
以上のように、コンポーネント設計原則は、パイプラインアーキテクチャの保守性を向上させるための、極めて強力な武器となります。
データパイプラインへのコンポーネント凝集原則の適応
上記で、CI/CDパイプラインにコンポーネントの凝集原則が威力を発揮することを説明しました。
同様にして、コンポーネントの凝集原則(CCP, CRP, REP)をデータパイプラインに適用することは、保守性の向上とデータ品質の向上に直結する、極めて強力なメカニズムです。
このアプローチは、カオスになりがちなETL/ELTプロセスを、
「単なるスクリプトの集まり」から「バージョン管理された、信頼できるデータ変換コンポーネントの集合体」
へと進化させます。
各原則のデータパイプラインへの適用
まず、各原則がデータパイプラインにおいて何を意味するかを定義します。
CCP (Common Closure Principle / 閉鎖性共通の原則)
「一緒に変更されるプロセスは、同じコンポーネントにまとめよ」
事例
「顧客のランク計算ロジック」が変更された際、transform_A
、transform_B
、validate_C
の3つのスクリプトが必ずセットで変更されるなら、それらは1つのCustomerRank_Component
にグルーピングします。
CRP (Common Reuse Principle / 共通再利用の原則)
「一緒に使われるプロセスは、同じコンポーネントにまとめよ」
事例
複数のデータプロダクトが「個人情報(PII)の匿名化」を行う際、
anonymize_name
とanonymize_address
を必ずセットで再利用するなら、それらを1つのPII_Anonymizer_Component
にグルーピングします。
REP (Reuse-Release Equivalence Principle / 再利用・リリース等価の原則)
「再利用の単位は、リリースの単位と等しくなければならない」
事例
上記のPII_Anonymizer_Component
を、v1.0.0
のように明確にバージョン付けしてリリースしていきます。
これにより、利用者は「どのバージョンの匿名化ロジックを使っているか」を明確に意識できます。
保守性を向上させるメカニズム
これらの原則は、変更時の 「影響範囲(ブラスト半径)」 を最小限に抑えることで、保守性を劇的に向上させます。
🛡️ 1. CCPによる「変更の封じ込め」
CCPに従っていないパイプライン(悪い例)では、「顧客ランク」のロジック変更が、無関係に見える複数の場所に散らばったスクリプトの修正を必要とします。
これは変更漏れやデグレードの温床です。
でも、CCPを適用すると、「顧客ランクに関する変更」 という単一のビジネス理由に対して、変更すべき場所がCustomerRank_Component
という1つのコンポーネントに限定されます。
これにより、
開発者は他のコンポーネント(例: PII_Anonymizer_Component
)が壊れる心配を一切せずに、安心して修正作業に集中できます。
これが保守性の向上に直結します。
📦 2. REP/CRPによる「安定した再利用」
REP/CRPに従っていないパイプライン(悪い例)では、誰かが共通スクリプト(例: anonymize_name
)を良かれと思って修正した瞬間、それを再利用していた全てのパイプラインが予期せず失敗する可能性があります。
でも、REP/CRPを適用すると、PII_Anonymizer_Component
は、v1.0.0
として安定的にリリースされます。
プラットフォームチームがロジックを改善してv1.1.0
をリリースしても、既存の利用者はv1.0.0
を使い続けることができます。
これにより、
利用者は自分たちの都合の良いタイミングでバージョンアップを選択でき、パイプライン全体の安定性が飛躍的に向上します。
データ品質を向上させるメカニズム
これが最も重要な点です。
凝集原則は、パイプラインの各コンポーネントを 「データ品質の保証人」 に変えます。
1. コンポーネントを「契約(Contract)」にする
REP/CRPによってグルーピングされたPII_Anonymizer:v1.1.0
というコンポーネントは、単なるスクリプトの集まりではありません。
それは、
「このコンポーネントの出力データは、最新のPII基準に従って100%匿名化されている」ことを保証する、契約(コントラクト)
となります。
これにより、組織全体で「匿名化ロジックの"揺れ"」がなくなり、一貫したデータ品質(PII漏洩の防止) が担保されます。
2. 「適応度関数(テスト)」の精度を高める
CCPによってグルーピングされたCustomerRank_Component
は、「顧客ランク計算」という単一の責務に特化しています。
これにより、このコンポーネント専用の 「適応度関数(=コンポーネントテスト)」 の精度を極限まで高めることができます。
例えば、
・「全てのランクがゴールド、シルバー、ブロンズのいずれかであること」
・「不正なランク(例: NULLやIron)が絶対に生成されないこと」
といった厳格なテストを、このコンポーネント内部で完結させられます。
データ品質の向上メカニズム
この「適応度関数」が、データ品質の最終防衛ラインとして機能します。
開発者がロジックを誤って修正し、不正なランク(データ品質の劣化)を生成するコードをコミットしてしまっても、CIパイプラインのコンポーネントテストの段階で即座に検知・ブロックされます。
これにより、データ品質の問題がDWHや下流のダッシュボードに到達する(シフトライト)のを未然に防ぎ、パイプラインの 「内部」(シフトレフト)で品質を保証 することが可能になります。