承接 andrebhas/laravel-brick 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

andrebhas/laravel-brick

最新稳定版本:v1.0.0

Composer 安装命令:

composer require andrebhas/laravel-brick

包简介

Build modular monolith Laravel applications with a solid foundation. Each module is a 'brick' that can be stacked wisely. Includes an optional internal bridge with async support, circuit breaker, and Pest testing.

README 文档

README

Build modular Laravel applications with a solid foundation. Each module is an independent, self-contained unit (a "brick") that can be composed into robust, scalable applications using Domain-Driven Design principles.

"Each module is a brick. Stack them wisely."

PHP Laravel License Tests

✨ Features

Feature Description
🏗 Modular Core Create isolated modules with a standard DDD structure
🌉 Internal Bridge Optional typed, validated cross-module communication
Async Calls Dispatch bridge calls to queues, poll results by job ID
🛡 Circuit Breaker Protect against cascading failures (closed/open/half-open)
🔗 Middleware Pipeline Logging, circuit breaker, caching per bridge call
🧪 Pest Testing Auto-generate test scaffolding with every module
📦 Module Dependencies Modules declare dependencies; enable/disable with checks
⚙️ Per-Module Config Each module has its own Config/config.php
🎨 Asset Publishing Copy or symlink module assets to public/bricks/
🚀 Octane Compatible No static state; fully safe for Laravel Octane

📋 Requirements

  • PHP 8.1 or higher
  • Laravel 9.x, 10.x, or 11.x

📦 Installation

composer require andrebhas/laravel-brick

The package uses Laravel's auto-discovery. The service provider is registered automatically.

Publish Configuration

php artisan vendor:publish --tag=brick-config

Publish Stubs (optional, to customise scaffolding)

php artisan vendor:publish --tag=brick-stubs

🗂 Module Structure

Layer-Driven Architecture (Default)

After running php artisan brick:make, each module follows this structure:

bricks/
└── Hotel/
    ├── module.json                  # Module manifest
    ├── Actions/
    │   └── CreateHotelAction.php
    ├── Models/
    │   └── Hotel.php
    ├── Repositories/
    │   ├── HotelRepositoryInterface.php
    │   └── EloquentHotelRepository.php
    ├── Services/
    │   └── HotelService.php
    ├── Bridge/                      # Optional: inter-module communication
    │   └── HotelBridge.php
    ├── Http/
    │   ├── Controllers/
    │   └── Requests/
    ├── Database/
    │   ├── Migrations/
    │   └── Seeders/
    ├── Routes/
    │   ├── api.php
    │   └── web.php
    ├── Resources/
    │   ├── views/
    │   ├── lang/
    │   └── assets/
    ├── Config/
    │   └── config.php
    ├── Providers/
    │   └── HotelServiceProvider.php
    ├── tests/
    │   ├── Pest.php
    │   ├── Feature/
    │   └── Unit/
    └── README.md

Feature-Driven Architecture

If you prefer to organizer your module by business features rather than technical layers, you can use the --features flag (or answer yes during the interactive prompt). This replaces the Actions directory with a Features directory:

bricks/
└── Hotel/
    ├── Features/
    │   └── CreateHotel/                     # A cohesive, isolated feature
    │       ├── CreateHotelAction.php        # Core logic
    │       ├── CreateHotelRequest.php       # Specific validation
    │       ├── CreateHotelResponse.php      # Formatting Output
    │       └── CreateHotelFeatureTest.php   # Feature tests lives here
    ├── Models/
    ├── Http/
    └── ... (other standard directories)

🚀 Quick Start

1. Create a Module

php artisan brick:make

Interactive flow:

Nama modul:
 > Hotel

Deskripsi modul (opsional):
 > Modul manajemen hotel

Versi modul [1.0.0]:
 >

Buat jembatan (bridge)? (yes/no) [no]:
 > yes

Buat rute API? (yes/no) [no]:
 > yes

Buat rute web? (yes/no) [no]:
 > no

Buat tes (Pest)? (yes/no) [yes]:
 > yes

Gunakan struktur Feature-Driven? (yes/no) [no]:
 > yes

Membuat modul...
✓ Module [Hotel] created successfully

Or non-interactively:

php artisan brick:make Hotel --with-bridge --with-api --with-tests --features

2. Register the Module's Namespace

Add to composer.json:

{
    "autoload": {
        "psr-4": {
            "Bricks\\Hotel\\": "bricks/Hotel/"
        }
    }
}

Then regenerate autoload:

composer dump-autoload

3. Enable the Module

php artisan brick:enable Hotel

4. Run Migrations

php artisan brick:migrate Hotel

📄 Module Manifest (module.json)

{
    "name": "Hotel",
    "description": "Modul manajemen hotel",
    "version": "1.0.0",
    "namespace": "Bricks\\Hotel",
    "active": true,
    "order": 0,
    "dependencies": ["Auth"],
    "bridge": {
        "class": "Bricks\\Hotel\\Bridge\\HotelBridge"
    },
    "author": {
        "name": "Andre Bhaskoro"
    }
}

🌉 Bridge: Cross-Module Communication

1. Implement the Bridge

namespace Bricks\Hotel\Bridge;

use AndreBhas\Brick\Brick\Bridge\Contracts\Bridgeable;

class HotelBridge implements Bridgeable
{
    public static function getBridgeName(): string
    {
        return 'Hotel';
    }

    public static function getExposedMethods(): array
    {
        return ['getById', 'checkAvailability'];
    }

    public function getById(int $id): array
    {
        // Fetch hotel data...
        return ['id' => $id, 'name' => 'Grand Hotel'];
    }

    public function checkAvailability(int $hotelId, string $date): bool
    {
        return true;
    }
}

2. Synchronous Call (from another module)

use AndreBhas\Brick\Brick\Bridge\InternalGateway;

$gateway = app(InternalGateway::class);

// Goes through middleware pipeline (logging, circuit breaker, etc.)
$hotel = $gateway->call('Hotel', 'getById', 42);

3. Asynchronous Call

// Dispatch to queue, get a job ID
$jobId = $gateway->callAsync('Hotel', 'getById', 42);

// Later, poll for the result
$result = $gateway->getAsyncResult($jobId);
// ['status' => 'success', 'result' => [...], 'completed_at' => '...']
// or
// ['status' => 'pending', 'queued_at' => '...']
// or
// ['status' => 'failed', 'error' => '...']

4. Generate Bridge for Existing Module

php artisan brick:make-bridge Hotel

🛡 Circuit Breaker

The circuit breaker prevents cascading failures when a module's bridge is unreliable.

States

  • Closed 🟢 — Normal operation; calls pass through
  • Open 🔴 — Failures exceeded threshold; calls rejected immediately
  • Half-Open 🟡 — Testing recovery with limited trial calls

Configuration (config/brick.php)

'circuit_breaker' => [
    'default' => [
        'threshold'              => 5,  // failures before opening
        'timeout'                => 30, // seconds before half-open
        'half_open_max_attempts' => 3,
    ],
],

Status Command

php artisan brick:bridge:status
+--------+------------+---------------+
| Bridge | State      | Failure Count |
+--------+------------+---------------+
| Hotel  | ● CLOSED   | 0             |
| Auth   | ● OPEN     | 7             |
+--------+------------+---------------+

Manual Reset

php artisan brick:bridge:status --reset=Hotel

🧪 Testing

Package Tests

composer test

Module Tests (Pest)

Each generated module includes its own tests directory with Pest setup:

# Run a specific module's tests
./vendor/bin/pest bricks/Hotel/tests/

Testing Helpers

BridgeFake – Mock bridge calls in tests

use AndreBhas\Brick\Brick\Testing\BridgeFake;
use AndreBhas\Brick\Brick\Bridge\InternalGateway;

$fake = new BridgeFake(
    container: app(),
    cache: app('cache')->store(),
    queue: app('queue'),
);

// Register a fake response
$fake->shouldReceive('Hotel', 'getById', fn ($id) => ['id' => $id, 'name' => 'Test Hotel']);

// Swap in the container
app()->instance(InternalGateway::class, $fake);

// ... run your code ...

// Assert calls
$fake->assertCalled('Hotel', 'getById', 1);
$fake->assertNotCalled('Hotel', 'delete');
$fake->assertNothingCalled(); // if no calls expected

CircuitBreakerFake – Simulate circuit states

use AndreBhas\Brick\Brick\Testing\CircuitBreakerFake;
use AndreBhas\Brick\Brick\Bridge\CircuitBreaker;

$fake = new CircuitBreakerFake();
$fake->forceOpen('Hotel');    // simulate open circuit
$fake->forceHalfOpen('Payment');
$fake->forceClose('Auth');

app()->instance(CircuitBreaker::class, $fake);

Generate Test File

# Feature test
php artisan brick:make-test Hotel HotelAvailability --type=feature

# Unit test
php artisan brick:make-test Hotel HotelPricing --type=unit

🎮 Artisan Commands Reference

Command Description
brick:make Create a new module interactively
brick:make-feature {module} {feature} Scaffold a full feature (Action, Request, etc)
brick:make-action {module} {name} Generate an Action class
brick:make-request {module} {name} Generate a FormRequest class
brick:make-response {module} {name} Generate a Response/Resource class
brick:make-job {module} {name} Generate a specific Job
brick:make-event {module} {name} Generate an Event
brick:make-listener {module} {name} Generate a Listener
brick:make-bridge {module} Generate bridge for a module
brick:make-test {module} {name} Generate a Pest test file
brick:bridge:status Show circuit breaker status for all bridges
brick:enable {module} Enable a module (checks dependencies)
brick:disable {module} Disable a module (checks dependents)
brick:publish-assets {module?} Publish module assets to public/
brick:migrate {module} Run module migrations
brick:rollback {module} Rollback module migrations
brick:refresh {module} Refresh (rollback + re-run) module migrations
brick:seed {module} Run module seeders

Command Options

# Enable with --force to skip dependency check
php artisan brick:enable Hotel --force

# Disable with --force to skip dependents check
php artisan brick:disable Hotel --force

# Publish assets as symlinks
php artisan brick:publish-assets Hotel --symlink

# Run specific number of rollback steps
php artisan brick:rollback Hotel --step=2

# Refresh and seed
php artisan brick:refresh Hotel --seed

# Run specific seeder class
php artisan brick:seed Hotel --class="Bricks\\Hotel\\Database\\Seeders\\HotelDemoSeeder"

⚙️ Configuration Reference

// config/brick.php

return [
    // Root directory for all modules
    'modules_path' => base_path('bricks'),

    'bridge' => [
        'enabled' => env('BRICK_BRIDGE_ENABLED', true),

        // Middleware applied to every synchronous bridge call
        'middlewares' => [
            \AndreBhas\Brick\Brick\Bridge\Middleware\LoggingMiddleware::class,
            \AndreBhas\Brick\Brick\Bridge\Middleware\CircuitBreakerMiddleware::class,
            // \AndreBhas\Brick\Brick\Bridge\Middleware\CacheMiddleware::class,
        ],

        'circuit_breaker' => [
            'default' => [
                'threshold' => 5,
                'timeout'   => 30,
                'half_open_max_attempts' => 3,
            ],
        ],

        'queue'       => env('BRICK_BRIDGE_QUEUE', 'default'),
        'cache_store' => env('BRICK_BRIDGE_CACHE', null),
        'cache_ttl'   => 3600,
    ],

    'assets' => [
        'publish_method' => env('BRICK_ASSETS_METHOD', 'copy'), // 'copy' or 'symlink'
        'public_path'    => public_path('bricks'),
    ],

    'dependencies' => [
        'auto_enable' => env('BRICK_AUTO_ENABLE_DEPENDENCIES', false),
    ],
];

Environment Variables

Variable Default Description
BRICK_BRIDGE_ENABLED true Enable/disable the bridge globally
BRICK_BRIDGE_QUEUE default Queue for async bridge calls
BRICK_BRIDGE_CACHE null Cache store for circuit breaker & async results
BRICK_ASSETS_METHOD copy Asset publishing method (copy or symlink)
BRICK_AUTO_ENABLE_DEPENDENCIES false Auto-enable missing dependencies

🔧 Adding Custom Middleware

Create a middleware class:

namespace App\Brick\Middleware;

use Closure;

class RateLimitMiddleware
{
    public function handle(Closure $next, string $bridge, string $method, array $args): mixed
    {
        // Rate limiting logic...
        return $next($bridge, $method, $args);
    }
}

Register in config/brick.php:

'middlewares' => [
    \AndreBhas\Brick\Brick\Bridge\Middleware\LoggingMiddleware::class,
    \AndreBhas\Brick\Brick\Bridge\Middleware\CircuitBreakerMiddleware::class,
    \App\Brick\Middleware\RateLimitMiddleware::class, // <-- add here
],

🧩 Module Dependencies

Declare dependencies in module.json:

{
    "name": "Booking",
    "dependencies": ["Hotel", "Auth"]
}

When enabling:

php artisan brick:enable Booking
# Error: Cannot enable [Booking]. Missing dependencies: Hotel (inactive).
# Use --force to override.

php artisan brick:enable Hotel
php artisan brick:enable Booking  # Now works

✅ Compatibility Notes

Package Notes
Spatie Media Library Use HasMedia trait directly in module models
Laravel Socialite Place OAuth controllers in Auth module's Http/Controllers/
Laravel Passport Register Passport in Auth module's service provider
Laravel Telescope Automatically captures all bridge calls via logging middleware
Laravel Debugbar No conflicts; bridge calls appear in the timeline
Laravel Octane Fully safe – no static state anywhere in the package

📁 Package Structure

laravel-brick/
├── src/
│   ├── BrickServiceProvider.php          # Auto-discovery entry point
│   └── Brick/
│       ├── Bridge/
│       │   ├── Contracts/Bridgeable.php  # Interface for bridge classes
│       │   ├── CircuitBreaker.php        # State machine (closed/open/half-open)
│       │   ├── InternalGateway.php       # Main call dispatcher
│       │   ├── Jobs/BridgeJob.php        # Queued async bridge job
│       │   └── Middleware/
│       │       ├── LoggingMiddleware.php
│       │       ├── CircuitBreakerMiddleware.php
│       │       └── CacheMiddleware.php
│       ├── Commands/                      # 11 Artisan commands
│       ├── Providers/
│       │   ├── BrickServiceProvider.php
│       │   └── BridgeServiceProvider.php
│       ├── Support/
│       │   ├── Module.php                # Module value object
│       │   └── ModuleManager.php         # Module discovery & state management
│       ├── Stubs/                        # Templates used by generators
│       │   ├── module/
│       │   └── bridge/
│       └── Testing/
│           ├── BridgeFake.php            # Test double for InternalGateway
│           └── CircuitBreakerFake.php    # Test double for CircuitBreaker
├── config/brick.php
├── tests/
├── composer.json
├── phpunit.xml
└── README.md

🤝 Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/my-feature
  3. Write tests for your changes
  4. Run the test suite: composer test
  5. Submit a pull request

📜 License

The MIT License (MIT). See LICENSE for details.

"Each module is a brick. Stack them wisely." — andrebhas/laravel-brick

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 8
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-03-06

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固