這是clean architecture的第9篇,也是具體案例的第一篇,本篇開始將會運用之前學到的知識,來構建一個小型的php應用。
本文為系列文章的第九篇,完成的目錄請查看Clean Architecture
計費系統
應用的uml簡圖如下:
應用的核心邏輯是:用戶會有多個訂單,然后固定周期對賬單進行結算。邏輯非常簡單,可以讓我們更專注于系統的架構上,那就讓我們開始系統的構建吧。
應用功能的構建流程
- 能夠新增用戶
- 能夠給用戶新增訂單
- 當需要給用戶出賬的時候能夠將訂單轉換為發票(invoices)
構建我們的領域模型
領域模型層只包含簡單的php class,此處只有3個Customer,Order,Invoice。我們先來構建我們的項目。
mkdir -p cleanphp-laravel/core/Domain/Entity
在cleanphp-laravel下新建composer.json文件,內容是:
{
"autoload": {
"psr-4": {
"CleanPhp\\Invoicer\\":"core/"
}
}
}
運行composer dump-autoload
產生vendor目錄。
創建Entity
每個entity都會有唯一的標識,于是我們新建一個AbstractEntity,將$id放入里面。
<?php namespace CleanPhp\Invoicer\Domain\Entity;
abstract class AbstractEntity {
protected $id;
/**
* @return mixed
*/
public function getId()
{
return $this->id;
}
/**
* @param mixed $id
*
* @return $this
*/
public function setId( $id )
{
$this->id = $id;
return $this;
}
}
同時定義Customer,Order,Invoice都繼承自該類,具體的可查看https://github.com/zhuanxuhit/php-clean-code
領域服務
領域服務包含了3個主要部分
- Repository Interface
- Factories
- Services
下面分別構建這3部分,先是Repository,對于每個Entity,都會有對應的Entity,而且會有一些公共的操作,于是提取出來,叫做RepositoryInterface,代碼如下:
<?php namespace CleanPhp\Invoicer\Domain\Repository;
interface RepositoryInterface {
public function getById( $id );
public function getAll();
public function persist( $entity );
public function begin();
public function commit();
}
其余的CustomerRepositoryInterface,OrderRepositoryInterface,InvoiceRepositoryInterface都擴展RepositoryInterface即可,其中OrderRepositoryInterface需要有個新的接口:getUninvoicedOrders來獲取未出賬單的訂單。
再來看Factory的構建,此處invoice的構建依賴于order,于是我們有下面的定義:
public function createFromOrder(Order $order);
至于怎么去實現這個方法,我們采用BDD(Behavior-Driven Development)的方式。
首先通過composer來包含庫
$ composer require --dev peridot-php/peridot peridot-php/leo peridot-php/peridot-prophecy-plugin
安裝完成后,通過執行
./vendor/bin/peridot
我們應該就能看到peridot本身的所有case執行成功。下一步讓我們來構建符合我們要求的case。
在根目錄下新建specs/domain/service/invoice-factory.spec.php文件,
describe( "InvoiceFactory", function () {
describe( "->createFromOrder()", function () {
it( "should return an order object", function () {
$order = new Order();
$factory = new InvoiceFactory();
$invoice = $factory->createFromOrder( $order );
expect( $invoice )->to->be->instanceof( Invoice::class );
} );
} );
} );
基于這種方式我們就能通過測試不斷去驅動完成我們的createFromOrder方法了,完整的測試case可以在github上查看。
最后我們來構建Invoicing services,同樣通過BDD的方式,先寫出case,新建specs/domain/service/invoice-factory.spec.php文件,里面的內容是:
describe( 'InvoicingService', function () {
describe( '->generateInvoices()', function () {
it( 'should query the repository for uninvoiced Orders');
it('should return an Invoice for each uninvoiced Order');
} );
} );
然后根據case再去實現generateInvoices()方法。
完整的例子,我們可以通過下面的命令查看
git clone https://github.com/zhuanxuhit/php-clean-code.git
git checkout 02-domain-services
總結
以上我們就完成了領域模型層和領域服務層的基本設計,到目前為止,我們的目錄如下:
在下一講中我們會引入Laravel,看怎么和具體的框架結合,盡請期待。
這是The Clean Architecture in PHP的第九篇,你的鼓勵是我繼續寫下去的動力,期待我們共同進步。