lunarstorm / laravel-ddd
Laravel 应用程序中领域驱动设计(DDD)模式工具包
Requires
- php: ^8.1|^8.2|^8.3
- illuminate/contracts: ^10.25|^11.0
- laravel/prompts: ^0.1.16
- lorisleiva/lody: ^0.5.0
- spatie/laravel-package-tools: ^1.13.0
Requires (Dev)
- larastan/larastan: ^2.0.1
- laravel/pint: ^1.0
- nunomaduro/collision: ^7.0|^8.1
- orchestra/testbench: ^8|^9.0
- pestphp/pest: ^2.34
- pestphp/pest-plugin-laravel: ^2.0
- phpstan/extension-installer: ^1.1
- phpstan/phpstan-deprecation-rules: ^1.0
- phpstan/phpstan-phpunit: ^1.0
README
Laravel-DDD 是一个工具包,用于在 Laravel 应用程序中支持领域驱动设计(DDD)。采用 DDD 的一个痛点是无法使用 Laravel 的原生 make
命令来生成领域对象,因为这些对象通常存储在 App\*
命名空间之外。此包旨在通过提供等效命令,如 ddd:model
、ddd:dto
、ddd:view-model
等,来填补这些空白。
安装
您可以通过 composer 安装此包
composer require lunarstorm/laravel-ddd
您可以使用 ddd:install
artisan 命令初始化此包。这将发布 配置文件,代表您在项目的 composer.json psr-4 自动加载配置中注册领域路径,并允许您在需要时发布生成器占位符进行自定义。
php artisan ddd:install
部署
在生产过程中,运行 ddd:cache
以 优化自动加载。
php artisan ddd:cache
版本兼容性
有关从 0.x 升级的更多详细信息,请参阅 UPGRADING。
用法
语法
所有领域生成器命令都使用以下语法
# Specifying the domain as an option php artisan ddd:{object} {name} --domain={domain} # Specifying the domain as part of the name (short-hand syntax) php artisan ddd:{object} {domain}:{name} # Not specifying the domain at all, which will then # prompt for it (with auto-completion) php artisan ddd:{object} {name}
可用命令
生成器
以下生成器目前可用,使用简写语法表示
# Generate a domain model php artisan ddd:model Invoicing:Invoice # Generate a domain model with factory php artisan ddd:model Invoicing:Invoice -f php artisan ddd:model Invoicing:Invoice --factory # Generate a domain factory php artisan ddd:factory Invoicing:InvoiceFactory php artisan ddd:factory Invoicing:InvoiceFactory --model=Invoice # optionally specifying the model # Generate a data transfer object php artisan ddd:dto Invoicing:LineItemPayload # Generates a value object php artisan ddd:value Shared:DollarAmount # Generates a view model php artisan ddd:view-model Invoicing:ShowInvoiceViewModel # Generates an action php artisan ddd:action Invoicing:SendInvoiceToCustomer # Extended Commands # These extend Laravel's respective make:* commands and places the objects into the domain layer php artisan ddd:cast Invoicing:MoneyCast php artisan ddd:channel Invoicing:InvoiceChannel php artisan ddd:command Invoicing:InvoiceDeliver php artisan ddd:event Invoicing:PaymentWasReceived php artisan ddd:exception Invoicing:InvoiceNotFoundException php artisan ddd:job Invoicing:GenerateInvoicePdf php artisan ddd:listener Invoicing:HandlePaymentReceived php artisan ddd:mail Invoicing:OverduePaymentReminderEmail php artisan ddd:notification Invoicing:YourPaymentWasReceived php artisan ddd:observer Invoicing:InvoiceObserver php artisan ddd:policy Invoicing:InvoicePolicy php artisan ddd:provider Invoicing:InvoiceServiceProvider php artisan ddd:resource Invoicing:InvoiceResource php artisan ddd:rule Invoicing:ValidPaymentMethod php artisan ddd:scope Invoicing:ArchivedInvoicesScope # Laravel 11+ only php artisan ddd:class Invoicing:Support/InvoiceBuilder php artisan ddd:enum Customer:CustomerType php artisan ddd:interface Customer:Contracts/Invoiceable php artisan ddd:trait Customer:Concerns/HasInvoices
生成的对象将放置在 配置文件 中指定的 ddd.namespaces.*
所述的适当领域命名空间中。
其他命令
# Show a summary of current domains in the domain folder php artisan ddd:list # Cache domain manifests (used for autoloading) php artisan ddd:cache # Clear the domain cache php artisan ddd:clear
高级用法
嵌套对象
对于任何 ddd:*
生成器命令,可以使用正斜杠指定嵌套对象。
php artisan ddd:model Invoicing:Payment/Transaction # -> Domain\Invoicing\Models\Payment\Transaction php artisan ddd:action Invoicing:Payment/ProcessTransaction # -> Domain\Invoicing\Actions\Payment\ProcessTransaction php artisan ddd:exception Invoicing:Payment/PaymentFailedException # -> Domain\Invoicing\Exceptions\Payment\PaymentFailedException
这对于没有固定命名空间的对象(例如 class
、interface
、trait
)至关重要,每个对象默认都有一个空命名空间。换句话说,这些对象来自领域的根。
php artisan ddd:class Invoicing:Support/InvoiceBuilder # -> Domain\Invoicing\Support\InvoiceBuilder php artisan ddd:interface Invoicing:Contracts/PayableByCreditCard # -> Domain\Invoicing\Contracts\PayableByCreditCard php artisan ddd:interface Invoicing:Models/Concerns/HasLineItems # -> Domain\Invoicing\Models\Concerns\HasLineItems
在运行时覆盖配置的命名空间
如果您需要出于某种原因在不同于 ddd.namespaces.*
中配置的命名空间下生成领域对象,您可以使用以 /
开头的绝对名称。这将从领域的根生成对象。
# The usual: generate a provider in the configured provider namespace php artisan ddd:provider Invoicing:InvoiceServiceProvider # -> Domain\Invoicing\Providers\InvoiceServiceProvider # Override the configured namespace at runtime php artisan ddd:provider Invoicing:/InvoiceServiceProvider # -> Domain\Invoicing\InvoiceServiceProvider # Generate an event inside the Models namespace (hypothetical) php artisan ddd:event Invoicing:/Models/EventDoesNotBelongHere # -> Domain\Invoicing\Models\EventDoesNotBelongHere # Deep nesting is supported php artisan ddd:exception Invoicing:/Models/Exceptions/InvoiceNotFoundException # -> Domain\Invoicing\Models\Exceptions\InvoiceNotFoundException
子域(嵌套域)
可以在接受领域选项的任何地方使用点符号指定子域。
# Domain/Reporting/Internal/ViewModels/MonthlyInvoicesReportViewModel php artisan ddd:view-model Reporting.Internal:MonthlyInvoicesReportViewModel # Domain/Reporting/Customer/ViewModels/MonthlyInvoicesReportViewModel php artisan ddd:view-model Reporting.Customer:MonthlyInvoicesReportViewModel # (supported by all commands where a domain option is accepted)
自定义
此包附带了一些具有见解(但合理)的默认配置。您可以通过发布 配置文件 和生成器占位符进行自定义
php artisan vendor:publish --tag="ddd-config" php artisan vendor:publish --tag="ddd-stubs"
请注意,扩展命令不会发布 ddd-specific 占位符,而是继承 Laravel 发布的应用程序级别的占位符。
领域自动加载和发现
可以使用 ddd.autoload
配置选项配置自动加载行为。默认情况下,领域提供程序、命令、策略和工厂会自动发现并注册。
'autoload' => [ 'providers' => true, 'commands' => true, 'policies' => true, 'factories' => true, ],
服务提供程序
当 ddd.autoload.providers
启用时,领域层中任何扩展 Illuminate\Support\ServiceProvider
的类都将自动注册为服务提供程序。
控制台命令
当启用 ddd.autoload.commands
时,在域层中继承自 Illuminate\Console\Command
的任何类,在控制台运行时都会自动注册为命令。
策略
当启用 ddd.autoload.policies
时,该包将注册一个自定义策略发现回调来解析域模型的策略名称,并在其他所有情况下回退到Laravel的默认设置。如果你的应用程序使用 Gate::guessPolicyNamesUsing()
实现了自己的策略发现,你应该将 ddd.autoload.policies
设置为 false
以确保它不会被覆盖。
工厂
当启用 ddd.autoload.factories
时,该包将注册一个自定义工厂发现回调来解析域模型的工厂名称,并在其他所有情况下回退到Laravel的默认设置。请注意,这不会影响使用 Lunarstorm\LaravelDDD\Factories\HasDomainFactory
特性的域模型。这很有用,尤其是对于域层中使用的标准 Illuminate\Database\Eloquent\Factories\HasFactory
特性的常规模型。
如果你的应用程序使用 Factory::guessFactoryNamesUsing()
实现了自己的工厂发现,你应该将 ddd.autoload.factories
设置为 false
以确保它不会被覆盖。
在自动加载过程中忽略路径
要指定在自动加载发现过程中应跳过的文件夹或路径,请将它们添加到 ddd.autoload_ignore
配置选项中。默认情况下,会忽略 Tests
和 Migrations
文件夹。
'autoload_ignore' => [ 'Tests', 'Database/Migrations', ],
这里指定的路径相对于每个域的根目录。例如,src/Domain/Invoicing/{path-to-ignore}
。如果需要更高级的过滤,可以在 AppServiceProvider 的 boot 方法中使用 DDD::filterAutoloadPathsUsing(callback $filter)
注册一个回调。
use Lunarstorm\LaravelDDD\Facades\DDD; use Symfony\Component\Finder\SplFileInfo; DDD::filterAutoloadPathsUsing(function (SplFileInfo $file) { if (basename($file->getRelativePathname()) === 'functions.php') { return false; } });
过滤器回调基于 Symfony 的 Finder 组件。
禁用自动加载
您可以通过在配置文件中将相应的自动加载选项设置为 false
来禁用自动加载,或者通过取消注释自动加载配置的全部内容。
// 'autoload' => [ // 'providers' => true, // 'commands' => true, // 'policies' => true, // 'factories' => true, // ],
生产环境中的自动加载
在生产环境中,您应该将自动加载清单缓存起来,作为应用程序部署过程的一部分,使用 ddd:cache
命令。这将加速域提供者和命令的自动发现和注册。如果需要,可以使用 ddd:clear
命令清除缓存。
配置文件
这是已发布的配置文件(ddd.php
)的内容。
return [ /* |-------------------------------------------------------------------------- | Domain Path |-------------------------------------------------------------------------- | | The path to the domain folder relative to the application root. | */ 'domain_path' => 'src/Domain', /* |-------------------------------------------------------------------------- | Domain Namespace |-------------------------------------------------------------------------- | | The root domain namespace. | */ 'domain_namespace' => 'Domain', /* |-------------------------------------------------------------------------- | Domain Object Namespaces |-------------------------------------------------------------------------- | | This value contains the default namespaces of generated domain | objects relative to the domain namespace of which the object | belongs to. | | e.g., Domain\Invoicing\Models\* | Domain\Invoicing\Data\* | Domain\Invoicing\ViewModels\* | Domain\Invoicing\ValueObjects\* | Domain\Invoicing\Actions\* | */ 'namespaces' => [ 'model' => 'Models', 'data_transfer_object' => 'Data', 'view_model' => 'ViewModels', 'value_object' => 'ValueObjects', 'action' => 'Actions', 'cast' => 'Casts', 'class' => '', 'channel' => 'Channels', 'command' => 'Commands', 'enum' => 'Enums', 'event' => 'Events', 'exception' => 'Exceptions', 'factory' => 'Database\Factories', 'interface' => '', 'job' => 'Jobs', 'listener' => 'Listeners', 'mail' => 'Mail', 'notification' => 'Notifications', 'observer' => 'Observers', 'policy' => 'Policies', 'provider' => 'Providers', 'resource' => 'Resources', 'rule' => 'Rules', 'scope' => 'Scopes', 'trait' => '', ], /* |-------------------------------------------------------------------------- | Base Model |-------------------------------------------------------------------------- | | The base class which generated domain models should extend. By default, | generated domain models will extend `Domain\Shared\Models\BaseModel`, | which will be created if it doesn't already exist. | */ 'base_model' => 'Domain\Shared\Models\BaseModel', /* |-------------------------------------------------------------------------- | Base DTO |-------------------------------------------------------------------------- | | The base class which generated data transfer objects should extend. By | default, generated DTOs will extend `Spatie\LaravelData\Data` from | Spatie's Laravel-data package, a highly recommended data object | package to work with. | */ 'base_dto' => 'Spatie\LaravelData\Data', /* |-------------------------------------------------------------------------- | Base ViewModel |-------------------------------------------------------------------------- | | The base class which generated view models should extend. By default, | generated domain models will extend `Domain\Shared\ViewModels\BaseViewModel`, | which will be created if it doesn't already exist. | */ 'base_view_model' => 'Domain\Shared\ViewModels\ViewModel', /* |-------------------------------------------------------------------------- | Base Action |-------------------------------------------------------------------------- | | The base class which generated action objects should extend. By default, | generated actions are based on the `lorisleiva/laravel-actions` package | and do not extend anything. | */ 'base_action' => null, /* |-------------------------------------------------------------------------- | Autoloading |-------------------------------------------------------------------------- | | Configure whether domain providers, commands, policies, and factories | should be auto-discovered and registered. | */ 'autoload' => [ /** * When enabled, any class within the domain layer extending `Illuminate\Support\ServiceProvider` * will be auto-registered as a service provider */ 'providers' => true, /** * When enabled, any class within the domain layer extending `Illuminate\Console\Command` * will be auto-registered as a command when running in console. */ 'commands' => true, /** * When enabled, the package will register a custom policy discovery callback to resolve policy names * for domain models, and fallback to Laravel's default for all other cases. */ 'policies' => true, /** * When enabled, the package will register a custom factory discovery callback to resolve factory names * for domain models, and fallback to Laravel's default for all other cases. */ 'factories' => true, ], /* |-------------------------------------------------------------------------- | Autoload Ignore Folders |-------------------------------------------------------------------------- | | Folders that should be skipped during autoloading discovery, | relative to the root of each domain. | | e.g., src/Domain/Invoicing/<folder-to-ignore> | | If more advanced filtering is needed, a callback can be registered | using the `DDD::filterAutoloadPathsUsing(callback $filter)` in | the AppServiceProvider's boot method. | */ 'autoload_ignore' => [ 'Tests', 'Database/Migrations', ], /* |-------------------------------------------------------------------------- | Caching |-------------------------------------------------------------------------- | | The folder where the domain cache files will be stored. Used for domain | autoloading. | */ 'cache_directory' => 'bootstrap/cache/ddd', ];
测试
composer test
变更日志
有关最近更改的更多信息,请参阅 变更日志。
安全漏洞
请查看 我们的安全策略 了解如何报告安全漏洞。
致谢
许可
MIT 许可证(MIT)。有关更多信息,请参阅 许可文件。