Laravelアプリケーションがそのドメインを叫ぶ
Laravelはあっという間にPHP開発のスターになりました。その功績はTaylor Otwellに感謝するべきです。これはただのフレームワークではありません;PHP開発者にとって複雑なタスクをシンプルに変える魔法の杖のようなものです。Laravelは豊富なツールキットで、PHP開発の考え方を革命的に変えました。もっとアクセスしやすく、楽しさも増しました。
でも重要なことがあります:時々、私たちの大切なLaravelアプリケーションはどれも似たり寄ったりになりがちです。これはLaravelの「間違い」ではなく、多くのフルスタックまたは機能が豊富なRESTフレームワークの固有の特徴です。たくさんのツールやすぐに使える構造を提供してくれるため、意識的に努力しなければ、機能は高いけれども独特の個性を放っていない(ドメインを叫んでいない)アプリケーションになりかねません。
ドメインを叫ぶ
第一印象が重要だって知っていますか?ソフトウェア開発の世界ではそれが黄金律です。それが「ドメインを叫ぶ」という考え方の出番です。これは有名なUncle Bobが「Screaming Architecture」と名づけた概念です。コツはシンプルです:ソフトウェアの構造をひと目で見ただけで、それが何をしようとしているのか大体のアイデアを掴めるようにすることです。まるでそのコード自体があなたを歓迎し、物語を語りかけてくれるかのようです。
ここにシンプルな例を示します:私たちのドメインはある物流会社です。コンテキストを説明する前に、このビジネスモデルのフォルダ構造を見てほしいです。それぞれのフォルダ名とクラス名を慎重に読んでみてください(ソフトウェアアーキテクチャはファイルの整理だけではありません)。
重要なこと:ファイル名は説明を明確にするためにポルトガル語で示されていますが、ソフトウェアは常に英語で書くべきです。
app/
│ ├── Models/
│ │ ├── Produto.php
│ │ ├── Armazem.php
│ │ ├── Transporte.php
│ │ └── Rastreio.php
│ │ ├── ProdutoTipo.php
│ │ ├── RastreioTipo.php
│ ├── Repositories/
│ │ ├── ProdutoRepository.php
│ │ ├── ArmazemRepository.php
│ │ ├── TransporteRepository.php
│ │ └── RastreioRepository.php
│ ├── Contracts/
│ │ ├── TransporteStrategyInterface.php
│ │ ├── RastreioStrategyInterface.php
│ │ ├── VeiculoInterface.php
│ │ └── RastreioInterface.php
│ ├── Strategies/
│ │ ├── TransporteStrategy.php
│ │ ├── RastreioStrategy.php
│ │ ├── Transporte/
│ │ │ ├── NavioStrategy.php
│ │ │ ├── CaminhaoStrategy.php
│ │ │ └── AviaoStrategy.php
│ │ └── Rastreio/
│ │ ├── RastreioTempoRealStrategy.php
│ │ └── RastreioPadraoStrategy.php
│ ├── Services/
│ │ ├── RastreioService.php
│ │ ├── TransporteService.php
│ │ ├── ArmazenamentoService.php
│ │ └── EntregaService.php
│ ├── Controllers/
│ │ ├── ProdutoController.php
│ │ ├── TransporteController.php
│ │ ├── RastreioController.php
│ │ └── ArmazenamentoController.php
│ ├── Factories/
│ │ ├── VeiculoFactory.php
│ │ └── RastreioFactory.php
│ └── Jobs/
│ ├── AtualizarStatusEntrega.php
│ ├── NotificarRastreioTempoReal.php
│ └── ProcessarEntregaProduto.php
物流ドメインを理解する
私たちの物流モデルでは主に輸送に関わっており、それは常に何かを動かしているということを意味しています - このケースでは商品です。私たちのビジネスモデルでは、これらの商品の追跡を提供しており、追跡タイプは使用される車両によって異なります。陸運、海運、航空運送のそれぞれには、独自の特性と必要性に応じて調整された追跡システムがあります。
私たちのLaravelで作られた物流向けプロジェクトでは、アーキテクチャの各要素は特定の機能を果たすだけでなく、それが属するドメインを強く反映しています。関心事の分離(SoC)と適切に実装された依存性注入を用いて、各パートの機能が明確に定義されたシステムを作り、物流業界の複雑な要求を反映しています。Modelsは製品や車両などの重要な実体を表している基盤です。依存性注入を通じて適応性のある輸送と追跡のStrategiesは、私たちのビジネスの動的な性質を示しています。Repositoriesはデータベースとの相互作用を専門としており、そのロジックをビジネスレイヤーから分離しています。
Servicesはビジネスロジックを処理し、Controllersはアクセスレイヤーの一部であり、ビジネスロジックとシステムの他のインターフェースとのやり取りを管理しています。この明確な責任の分離は物流の特定のニーズに対応するだけでなく、システムの目的を明確に伝えています。ソフトウェアの各コンポーネントはビジネスの特定の側面に完全に整列しており、直感的で効率的なアーキテクチャです。これは物流ドメインの忠実な表現であり、システムの開発と運用を容易にします。
比較として、下記にLaravelがそのドメインを管理するために「押し付ける」一般的なアーキテクチャを示します。
app/
│ ├── Http/
│ │ ├── Controllers/
│ │ │ ├── ProdutoController.php
│ │ │ ├── TransporteController.php
│ │ │ ├── RastreioController.php
│ │ │ └── ArmazenamentoController.php
│ │ │
│ │ ├── Middleware/
│ │ │ ├── AuthMiddleware.php
│ │ │ ├── LoggingMiddleware.php
│ │ │ └── ...
│ │ │
│ │ ├── Requests/
│ │ │ ├── ProdutoRequest.php
│ │ │ ├── TransporteRequest.php
│ │ │ ├── RastreioRequest.php
│ │ │ └── ArmazenamentoRequest.php
│ │ │
│ │ └── ...
│ │
│ ├── Models/
│ │ ├── Produto.php
│ │ ├── Transporte.php
│ │ ├── Rastreio.php
│ │ └── ...
│ │
│ └── ...
上記のケースでは、よりジェネリックなアーキテクチャが見られ、ドメインに対応しているかもしれませんが、この運送業者がいくつかの側面で何をしているのかはまだはっきりしません。
生きたドキュメント
Screaming Architectureを私たちの物流ドメインに適用すると、コードの構造が明確で生きたドキュメントになります。例えば、船舶、トラック、航空機など、異なるタイプの輸送手段があり、それぞれ独自の追跡戦略がある場合、コードのアーキテクチャはこの複雑さを明白に反映しています。
新しい開発者がソースコードをナビゲートするときに、各輸送手段のタイプとそれに関連する追跡戦略のフォルダとクラスを簡単に特定できると想像してみてください。これは外部ドキュメントの必要性をなくすものではありませんが、このドキュメントをビジネスドメイン自体についてのものにし、コードの各部分が何をしているのかについてではないため、アプリケーションがビジネスのこれらの側面をどのように扱っているのかをコードの構造自体が伝えるようになります。
このアーキテクチャの明確さは、新しいチームメンバーの統合プロセスを単純化するだけでありませんが、アプリケーションの継続的な開発、保守、拡張の効率も向上させます。
テスト可能性
ドメインを「叫ぶ」アーキテクチャを持つと、コードのテストがよりシンプルで理解しやすくなります。私たちの物流の文脈では、船、トラック、飛行機の異なる追跡戦略があります。
明確なアーキテクチャを持つことで、これらの戦略のそれぞれについて簡単にテストを作成することができます。例えば、船の追跡戦略が正しく動作していることを確認するためのテストを書くことができます。これには、船の追跡動作を異なる状況でシミュレートして、結果が正しいかどうかを検証することが含まれます。以下は例です:
<?php
use PHPUnit\Framework\TestCase;
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;
class NavioRastreioStrategyTest extends TestCase
{
public function testDeveRetornarCidadeEstadoLink()
{
$mockResponse = new Response(200, [], json_encode([
'latitude' => 42.12345,
'longitude' => -71.98765,
]));
$mock = new MockHandler([$mockResponse]);
$handlerStack = HandlerStack::create($mock);
$httpClient = new Client(['handler' => $handlerStack]);
$navioMock = $this->getMockBuilder(Navio::class)
->disableOriginalConstructor()
->getMock();
$navioMock->codigo = 'COD123';
$navioMock->tipo_mercadoria = 'AD234BC';
// httpClientはオプションのパラメータです
// 戦略が呼び出されたときに自動的に設定されますが、
// テスト可能にするためにこのクライアントをモックすることができます
$strategy = new RastreioStrategy($httpClient);
// 自動的にNavioRastreioStrategyを呼び出します
$result = $strategy->rastrear($navioMock);
$data = json_decode($result, true);
$this->assertEquals('Exemplo Cidade', $data['cidade']['nome']);
$this->assertEquals('Exemplo Estado', $data['estado']['nome']);
$this->assertEquals('https://maps.google.com/?q=42.12345,-71.98765', $data['googleMaps']['link']);
}
}
このテストではビジネスルールをテストすることができますが、おそらくはっきりした結果が得られるかどうかは別として、私たちのアーキテクチャがいかに明らかに私たちのアプリケーションの特定の用途を示し、特定のサブドメインである船の追跡をテストするためのテストを非常に簡単に作成することができました。
結論
ソフトウェアが解決しようとしている問題を明確に伝えるアーキテクチャを使用することは、長寿を目指すシステムにとって有意義な利点です。将来、Laravelが廃止されるという懸念があるかもしれませんが、それは考慮していません。
このアプローチの利点には次のようなものがあります:
- 生きたドキュメント: ソフトウェアのドキュメンテーションは問題のドメインに焦点を当てるため、より関連性が高く、便利です。
- テスト可能性: テスト駆動開発を含むテストの実装により、フレームワークの新しい安定版のリリースごとにソフトウェアを簡単に更新できます。
- スケーラビリティ: 叫ぶアーキテクチャは開発者が各コンポーネントが何を表しているかを理解するためのガイドとなるだけでなく、将来的にどのように実装すべきかを暗黙のうちに示しているため、新しい
こちらの記事はdev.toの良い記事を日本人向けに翻訳しています。
https://dev.to/pedrovian4/aplicacoes-laravel-que-gritam-seu-dominio-2h5i