Skip to content

Commit

Permalink
Merge branch 'next' into feature/domain-autoloader
Browse files Browse the repository at this point in the history
  • Loading branch information
pelmered authored Mar 25, 2024
2 parents a0a5c06 + 2363483 commit a418b1d
Show file tree
Hide file tree
Showing 60 changed files with 1,034 additions and 480 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/phpstan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.1'
php-version: '8.2'
coverage: none

- name: Install composer dependencies
Expand Down
11 changes: 4 additions & 7 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
push:
branches: [main]
pull_request:
branches: [main]
branches: [main, next]

jobs:
test:
Expand All @@ -14,18 +14,15 @@ jobs:
matrix:
os: [ubuntu-latest, windows-latest]
php: [8.3, 8.2, 8.1]
laravel: [11.*, 10.*, 9.*]
laravel: [11.*, 10.25.*]
stability: [prefer-lowest, prefer-stable]
include:
- laravel: 11.*
testbench: 9.*
carbon: ^3.0
- laravel: 10.*
- laravel: 10.25.*
testbench: 8.*
carbon: ^2.63
- laravel: 9.*
testbench: 7.*
carbon: ^2.63
carbon: 2.*
exclude:
- laravel: 11.*
php: 8.1
Expand Down
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,37 @@

All notable changes to `laravel-ddd` will be documented in this file.

## [Unversioned]
### Added
- `ddd:list` to show a summary of current domains in the domain folder.
- Additional generators that extend Laravel's generators and funnel the generated objects into the domain layer:
- `ddd:cast {domain}:{name}`
- `ddd:channel {domain}:{name}`
- `ddd:command {domain}:{name}`
- `ddd:enum {domain}:{name}` (Laravel 11 only)
- `ddd:event {domain}:{name}`
- `ddd:exception {domain}:{name}`
- `ddd:job {domain}:{name}`
- `ddd:listener {domain}:{name}`
- `ddd:mail {domain}:{name}`
- `ddd:notification {domain}:{name}`
- `ddd:observer {domain}:{name}`
- `ddd:policy {domain}:{name}`
- `ddd:provider {domain}:{name}`
- `ddd:resource {domain}:{name}`
- `ddd:rule {domain}:{name}`
- `ddd:scope {domain}:{name}`
- For all `ddd:*` generator commands, if a domain wasn't specified, prompt for the domain with auto-completion (based on current domains in the domain folder).

### Changed
- (BREAKING) `ddd:*` commands no longer receive a dedicated domain argument. Example: `ddd:action Invoicing CreateInvoice` can be one of:
- `ddd:action CreateInvoice --domain=Invoicing` (this takes precedence).
- Shorthand syntax: `ddd:action Invoicing:CreateInvoice`.
- Or simply `ddd:action CreateInvoice` to be prompted for the domain.

### Chore
- Dropped Laravel 9 support.

## [0.10.0] - 2024-03-23
### Added
- Add `ddd.domain_path` and `ddd.domain_namespace` to config, to specify the path to the domain layer and root domain namespace more explicitly (replaces the previous `ddd.paths.domains` config).
Expand Down
110 changes: 85 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,15 @@
[![GitHub Code Style Action Status](https://img.shields.io/github/actions/workflow/status/lunarstorm/laravel-ddd/fix-php-code-style-issues.yml?branch=main&label=code%20style&style=flat-square)](https://github.com/lunarstorm/laravel-ddd/actions?query=workflow%3A"Fix+PHP+code+style+issues"+branch%3Amain)
[![Total Downloads](https://img.shields.io/packagist/dt/lunarstorm/laravel-ddd.svg?style=flat-square)](https://packagist.org/packages/lunarstorm/laravel-ddd)

Laravel-DDD is a toolkit to support domain driven design (DDD) patterns in Laravel applications. One of the pain points when adopting DDD is the inability to use Laravel's native `make:model` artisan command to properly generate domain models, since domain models are not intended to be stored in the `App/Models/*` namespace. This package aims to fill the gaps by providing an equivalent command, `ddd:model`, plus a few more.
Laravel-DDD is a toolkit to support domain driven design (DDD) patterns in Laravel applications. One of the pain points when adopting DDD is the inability to use Laravel's native `make:model` artisan command to properly generate domain models, since domain models are not intended to be stored in the `App/Models/*` namespace. This package aims to fill the gaps by providing an equivalent command, `ddd:model`, plus many more.

> :warning: **Disclaimer**: This package is subject to frequent design changes as it evolves towards a stable v1.0 release. It is currently being tested and fine tuned within Lunarstorm's client projects.
## Version Compatibility
Laravel | LaravelDDD
:-----------|:-----------
9.x | 0.x
<10.25 | 0.x
10.25+ | 1.x
11.x | 1.x

## Installation

Expand All @@ -24,56 +30,94 @@ php artisan ddd:install

## Usage

The following generator commands are currently available:
Command syntax:
```bash
# 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
# you to enter the domain name (with auto-completion)
php artisan ddd:{object} {name}
```

The following generators are currently available, shown using short-hand syntax:
```bash
# Generate a domain model
php artisan ddd:model {domain} {name}
php artisan ddd:model {domain}:{name}

# Generate a domain model with factory
php artisan ddd:model {domain} {name} -f
php artisan ddd:model {domain} {name} --factory
php artisan ddd:model {domain}:{name} -f
php artisan ddd:model {domain}:{name} --factory

# Generate a domain factory
php artisan ddd:factory {domain} {name} [--model={model}]
php artisan ddd:factory {domain}:{name} [--model={model}]

# Generate a data transfer object
php artisan ddd:dto {domain} {name}
php artisan ddd:dto {domain}:{name}

# Generates a value object
php artisan ddd:value {domain} {name}
php artisan ddd:value {domain}:{name}

# Generates a view model
php artisan ddd:view-model {domain} {name}
php artisan ddd:view-model {domain}:{name}

# Generates an action
php artisan ddd:action {domain} {name}
php artisan ddd:action {domain}:{name}

# Extended Commands
# (extends Laravel's make:* generators and funnels the objects into the domain layer)
php artisan ddd:cast {domain}:{name}
php artisan ddd:channel {domain}:{name}
php artisan ddd:command {domain}:{name}
php artisan ddd:enum {domain}:{name} # Requires Laravel 11+
php artisan ddd:event {domain}:{name}
php artisan ddd:exception {domain}:{name}
php artisan ddd:job {domain}:{name}
php artisan ddd:listener {domain}:{name}
php artisan ddd:mail {domain}:{name}
php artisan ddd:notification {domain}:{name}
php artisan ddd:observer {domain}:{name}
php artisan ddd:policy {domain}:{name}
php artisan ddd:provider {domain}:{name}
php artisan ddd:resource {domain}:{name}
php artisan ddd:rule {domain}:{name}
php artisan ddd:scope {domain}:{name}
```

Examples:
```bash
php artisan ddd:model Invoicing LineItem # Domain/Invoicing/Models/LineItem
php artisan ddd:model Invoicing LineItem -f # Domain/Invoicing/Models/LineItem + Database/Factories/Invoicing/LineItemFactory
php artisan ddd:factory Invoicing LineItemFactory # Database/Factories/Invoicing/LineItemFactory
php artisan ddd:dto Invoicing LinePayload # Domain/Invoicing/Data/LinePayload
php artisan ddd:value Shared Percentage # Domain/Shared/ValueObjects/Percentage
php artisan ddd:view-model Invoicing ShowInvoiceViewModel # Domain/Invoicing/ViewModels/ShowInvoiceViewModel
php artisan ddd:action Invoicing SendInvoiceToCustomer # Domain/Invoicing/Actions/SendInvoiceToCustomer
php artisan ddd:model Invoicing:LineItem # Domain/Invoicing/Models/LineItem
php artisan ddd:model Invoicing:LineItem -f # Domain/Invoicing/Models/LineItem + Database/Factories/Invoicing/LineItemFactory
php artisan ddd:factory Invoicing:LineItemFactory # Database/Factories/Invoicing/LineItemFactory
php artisan ddd:dto Invoicing:LinePayload # Domain/Invoicing/Data/LinePayload
php artisan ddd:value Shared:Percentage # Domain/Shared/ValueObjects/Percentage
php artisan ddd:view-model Invoicing:ShowInvoiceViewModel # Domain/Invoicing/ViewModels/ShowInvoiceViewModel
php artisan ddd:action Invoicing:SendInvoiceToCustomer # Domain/Invoicing/Actions/SendInvoiceToCustomer
```

Subdomains (nested domains) can be specified with dot notation:
```bash
php artisan ddd:model Invoicing.Customer CustomerInvoice # Domain/Invoicing/Customer/Models/CustomerInvoice
php artisan ddd:factory Invoicing.Customer CustomerInvoice # Database/Factories/Invoicing/Customer/CustomerInvoiceFactory
php artisan ddd:model Invoicing.Customer:CustomerInvoice # Domain/Invoicing/Customer/Models/CustomerInvoice
php artisan ddd:factory Invoicing.Customer:CustomerInvoice # Database/Factories/Invoicing/Customer/CustomerInvoiceFactory
# (supported by all generator commands)
```

### Other Commands
```bash
# Show a summary of current domains in the domain folder
php artisan ddd:list
```

This package ships with opinionated (but sensible) configuration defaults. If you need to customize, you may do so by publishing the config file and generator stubs as needed:

```bash
php artisan vendor:publish --tag="ddd-config"
php artisan vendor:publish --tag="ddd-stubs"
```
Note that the extended commands do not publish ddd-specific stubs, and inherit the respective application-level stubs published by Laravel.

This is the content of the published config file (`ddd.php`):

Expand Down Expand Up @@ -117,11 +161,27 @@ return [
|
*/
'namespaces' => [
'models' => 'Models',
'data_transfer_objects' => 'Data',
'view_models' => 'ViewModels',
'value_objects' => 'ValueObjects',
'actions' => 'Actions',
'model' => 'Models',
'data_transfer_object' => 'Data',
'view_model' => 'ViewModels',
'value_object' => 'ValueObjects',
'action' => 'Actions',
'cast' => 'Casts',
'channel' => 'Channels',
'command' => 'Commands',
'enum' => 'Enums',
'event' => 'Events',
'exception' => 'Exceptions',
'job' => 'Jobs',
'listener' => 'Listeners',
'mail' => 'Mail',
'notification' => 'Notifications',
'observer' => 'Observers',
'policy' => 'Policies',
'provider' => 'Providers',
'resource' => 'Resources',
'rule' => 'Rules',
'scope' => 'Scopes',
],

/*
Expand Down
16 changes: 8 additions & 8 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,20 @@
],
"require": {
"php": "^8.1|^8.2|^8.3",
"spatie/laravel-package-tools": "^1.13.0",
"illuminate/contracts": "^9.0|^10.0|^11.0"
"illuminate/contracts": "^10.25|^11.0",
"laravel/prompts": "^0.1.16",
"spatie/laravel-package-tools": "^1.13.0"
},
"require-dev": {
"laravel/pint": "^1.0",
"nunomaduro/collision": "^6.0|^7.0|^8.1",
"nunomaduro/collision": "^7.0|^8.1",
"larastan/larastan": "^2.0.1",
"orchestra/testbench": "^7|^8|^9.0",
"pestphp/pest": "^1.22|^2.0",
"pestphp/pest-plugin-laravel": "^1.1|^2.0",
"orchestra/testbench": "^8|^9.0",
"pestphp/pest": "^2.0",
"pestphp/pest-plugin-laravel": "^2.0",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan-deprecation-rules": "^1.0",
"phpstan/phpstan-phpunit": "^1.0",
"phpunit/phpunit": "^9.5|^10"
"phpstan/phpstan-phpunit": "^1.0"
},
"autoload": {
"psr-4": {
Expand Down
26 changes: 21 additions & 5 deletions config/ddd.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,27 @@
|
*/
'namespaces' => [
'models' => 'Models',
'data_transfer_objects' => 'Data',
'view_models' => 'ViewModels',
'value_objects' => 'ValueObjects',
'actions' => 'Actions',
'model' => 'Models',
'data_transfer_object' => 'Data',
'view_model' => 'ViewModels',
'value_object' => 'ValueObjects',
'action' => 'Actions',
'cast' => 'Casts',
'channel' => 'Channels',
'command' => 'Commands',
'enum' => 'Enums',
'event' => 'Events',
'exception' => 'Exceptions',
'job' => 'Jobs',
'listener' => 'Listeners',
'mail' => 'Mail',
'notification' => 'Notifications',
'observer' => 'Observers',
'policy' => 'Policies',
'provider' => 'Providers',
'resource' => 'Resources',
'rule' => 'Rules',
'scope' => 'Scopes',
'factories' => 'Database\Factories',
],

Expand Down
40 changes: 20 additions & 20 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.2/phpunit.xsd" backupGlobals="false" bootstrap="vendor/autoload.php" colors="true" processIsolation="false" stopOnFailure="false" executionOrder="random" failOnWarning="true" failOnRisky="true" failOnEmptyTestSuite="true" beStrictAboutOutputDuringTests="true" cacheDirectory=".phpunit.cache" backupStaticProperties="false">
<testsuites>
<testsuite name="Laravel-DDD Test Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
<coverage>
<report>
<html outputDirectory="build/coverage"/>
<text outputFile="build/coverage.txt"/>
<clover outputFile="build/logs/clover.xml"/>
</report>
</coverage>
<logging>
<junit outputFile="build/report.junit.xml"/>
</logging>
<source>
<include>
<directory suffix=".php">./src</directory>
</include>
</source>
<testsuites>
<testsuite name="Laravel-DDD Test Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
<coverage>
<report>
<html outputDirectory="build/coverage"/>
<text outputFile="build/coverage.txt"/>
<clover outputFile="build/logs/clover.xml"/>
</report>
</coverage>
<logging>
<junit outputFile="build/report.junit.xml"/>
</logging>
<source>
<include>
<directory suffix=".php">./src</directory>
</include>
</source>
</phpunit>
35 changes: 35 additions & 0 deletions src/Commands/Concerns/CanPromptForDomain.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace Lunarstorm\LaravelDDD\Commands\Concerns;

use Illuminate\Support\Str;
use Lunarstorm\LaravelDDD\Support\DomainResolver;

use function Laravel\Prompts\suggest;

trait CanPromptForDomain
{
protected function promptForDomainName(): string
{
$choices = collect(DomainResolver::domainChoices())
->mapWithKeys(fn ($name) => [Str::lower($name) => $name]);

// Prompt for the domain
$domainName = suggest(
label: 'What is the domain?',
options: fn ($value) => collect($choices)
->filter(fn ($name) => Str::contains($name, $value, ignoreCase: true))
->toArray(),
placeholder: 'Start typing to search...',
required: true
);

// Normalize the case of the domain name
// if it is an existing domain.
if ($match = $choices->get(Str::lower($domainName))) {
$domainName = $match;
}

return $domainName;
}
}
Loading

0 comments on commit a418b1d

Please sign in to comment.