Skip to content

Commit bcff346

Browse files
Adding rules for modular monolith architectures (#16)
1 parent 2e8b13a commit bcff346

39 files changed

+1925
-8
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ See individual rule documentation for detailed configuration examples. A [full c
1919
### Architecture Rules
2020

2121
- [Dependency Constraints Rule](docs/rules/Dependency-Constraints-Rule.md)
22+
- [Modular Architecture Rule](docs/rules/Modular-Architecture-Rule.md)
23+
- [Circular Module Dependency Rule](docs/rules/Circular-Module-Dependency-Rule.md)
2224
- [Forbidden Namespaces Rule](docs/rules/Forbidden-Namespaces-Rule.md)
2325
- [Class Must Be Readonly Rule](docs/rules/Class-Must-Be-Readonly-Rule.md)
2426
- [Class Must Be Final Rule](docs/rules/Class-Must-Be-Final-Rule.md)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Capability\Billing\Application;
6+
7+
use App\Capability\UserManagement\UserManagementFacade;
8+
9+
/**
10+
* Creates circular dependency:
11+
* Billing → UserManagement
12+
* UserManagement would depend on ProductCatalog (in ValidCrossModule)
13+
* If we make ProductCatalog depend on Billing, we get a cycle
14+
*/
15+
class CircularDep
16+
{
17+
public function __construct(
18+
private UserManagementFacade $userManagement
19+
) {
20+
}
21+
}
22+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Capability\Billing;
6+
7+
/**
8+
* Facade for Billing module
9+
*/
10+
class BillingFacade
11+
{
12+
public function processBilling(): void
13+
{
14+
}
15+
}
16+
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Capability\ProductCatalog\Application;
6+
7+
use App\Capability\Billing\BillingFacade;
8+
9+
/**
10+
* This creates a circular dependency when combined with Billing/Application/CircularDep.php:
11+
* ProductCatalog → Billing → UserManagement → ProductCatalog (via ValidCrossModule)
12+
*
13+
* Wait, let me reconsider. ValidCrossModule has:
14+
* ProductCatalog → UserManagement
15+
*
16+
* CircularDep has:
17+
* Billing → UserManagement
18+
*
19+
* This file has:
20+
* ProductCatalog → Billing
21+
*
22+
* So we don't quite have a cycle yet. Let me think...
23+
* We need: A → B → C → A
24+
*
25+
* Let's make it simpler:
26+
* - ProductCatalog → UserManagement (ValidCrossModule)
27+
* - UserManagement → Billing (need to create this)
28+
* - Billing → ProductCatalog (this file creates it)
29+
*/
30+
class CreateCircular
31+
{
32+
public function __construct(
33+
private BillingFacade $billing
34+
) {
35+
}
36+
}
37+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Capability\ProductCatalog\Application;
6+
7+
use App\Capability\UserManagement\UserManagementException;
8+
9+
/**
10+
* Invalid: Importing exception from another module
11+
*/
12+
class InvalidCrossModule
13+
{
14+
public function __construct()
15+
{
16+
}
17+
}
18+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Capability\ProductCatalog\Application;
6+
7+
use App\Capability\UserManagement\UserManagementDto;
8+
9+
/**
10+
* This imports a DTO class from another module.
11+
* - With default config: This is INVALID (only Facade, FacadeInterface, Input, Result allowed)
12+
* - With custom pattern '/Dto$/': This is VALID
13+
*/
14+
class UseCustomDto
15+
{
16+
public function __construct(
17+
private UserManagementDto $dto
18+
) {
19+
}
20+
}
21+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Capability\ProductCatalog\Application;
6+
7+
use App\Capability\UserManagement\UserManagementFacade;
8+
use App\Capability\UserManagement\UserManagementFacadeInterface;
9+
use App\Capability\UserManagement\Application\UseCases\CreateUser\CreateUserInput;
10+
use App\Capability\UserManagement\Application\UseCases\CreateUser\CreateUserResult;
11+
12+
/**
13+
* Valid cross-module imports: Facade, FacadeInterface, Input, Result
14+
*/
15+
class ValidCrossModule
16+
{
17+
public function __construct(
18+
private UserManagementFacadeInterface $userManagement
19+
) {
20+
}
21+
22+
public function doSomething(): void
23+
{
24+
$input = new CreateUserInput('123', 'John Doe');
25+
$result = $this->userManagement->createUser($input);
26+
}
27+
}
28+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Capability\ProductCatalog\Domain\Model;
6+
7+
/**
8+
* Valid domain entity
9+
*/
10+
class Product
11+
{
12+
public function __construct(
13+
private string $id,
14+
private string $name
15+
) {
16+
}
17+
18+
public function getId(): string
19+
{
20+
return $this->id;
21+
}
22+
23+
public function getName(): string
24+
{
25+
return $this->name;
26+
}
27+
}
28+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Capability\ProductCatalog;
6+
7+
/**
8+
* Facade for ProductCatalog module
9+
*/
10+
class ProductCatalogFacade
11+
{
12+
public function getProducts(): array
13+
{
14+
return [];
15+
}
16+
}
17+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Capability\UserManagement\Application;
6+
7+
use App\Capability\ProductCatalog\ProductCatalogFacade;
8+
9+
/**
10+
* This completes the circular dependency:
11+
* - ProductCatalog → Billing (CreateCircular.php in ProductCatalog, analyzed first)
12+
* - Billing → UserManagement (CircularDep.php in Billing, analyzed second)
13+
* - UserManagement → ProductCatalog (this file, analyzed third, creates cycle)
14+
*
15+
* Expected cycle: ProductCatalog → Billing → UserManagement → ProductCatalog
16+
*/
17+
class CreateCircularToUserManagement
18+
{
19+
public function __construct(
20+
private ProductCatalogFacade $productCatalog
21+
) {
22+
}
23+
}
24+

0 commit comments

Comments
 (0)