samaphp/laravel-bounded
Composer 安装命令:
composer require samaphp/laravel-bounded
包简介
Boundary enforcement for Laravel — invokable controllers, transaction commit boundaries, log event keys, DTO discipline, facade zones, middleware foreclosure. Validators run at artisan boot; static rules ship for PHPStan and Deptrac.
README 文档
README
An opinionated guardrail layer for Laravel: architectural conventions enforced as mechanical CI gates. The build either passes or the architecture is wrong. Runtime validators, PHPStan rules, and Deptrac config fail the build when conventions drift. Built for codebases where humans and AI agents both write code, and PR review is the bottleneck.
What this solves
Three failure modes:
- Drift accumulation. Laravel allows closures in routes, inline
Validator::make, helpers files, observers, model lifecycle hooks. Each is fine in isolation; each is a place for the codebase to drift. Bounded forbids them and fails the build the moment one appears. - Reviewer load. Humans cannot keep up with structural drift in PR review. Mechanical gates run on every commit.
- Agent normalization. AI agents copy whatever pattern they see. One rogue feature folder normalizes a bad pattern across the codebase. Bounded rejects the wrong shape before it lands.
What this rejects
Reject this package if any of these are dealbreakers:
- Layer-folder, not feature-folder. Code lives under
app/Services/{Domain}/,app/Repositories/{Domain}/,app/Http/Controllers/{Domain}/. The layer is the top dimension. Feature-folder layouts (each feature owning its own controller-service-repository tree) are not supported. - No exceptions. If a rule applies, it always applies. No carve-out for small projects, trivial cases, or "fix it later." Wrong rules get changed for everyone.
- No magic. No observers, no model lifecycle hooks (
boot/booted), noEventSubscriberInterface, noapp/helpers.php, no closures in routes. Every action has a visible call site.
What it enforces
Boot-time validators (run on arch:validate):
ZonePartition—app/paths assigned tologic,framework_bridge, orrepositoryzones. Boot fails if any path is in multiple zones.SingleActionController(app/Http/Controllers) — each concrete controller must befinal, have__invoke, no other public methods, noControllersuffix. Skips abstract classes.TestParity— two-layer detection: (1) every concrete class underapp/Http/Controllers/,app/Console/Commands/,app/Jobs/must have a mirror test undertests/Feature/ortests/Unit/(folder scan, parentage-agnostic — vendor-base entrypoints in canonical folders are caught); (2) for commands/jobs, a cross-app/misplacement scan flags classes transitively extendingIlluminate\Console\Commandor implementingIlluminate\Contracts\Queue\ShouldQueuethat live outside the canonical folder. Silent when none exist anywhere (fresh Laravel scaffolds withoutapp/Console/Commandsorapp/Jobspass). Skips abstract classes. Misplacement scan is shallow — vendor base classes outsideapp/are not traced (move such classes into the canonical folder, where the folder scan covers them); interface inheritance is not walked.NoListeners(app/Listeners,app/Observers, all ofapp/) — no files in those directories; no class anywhere implementsEventSubscriberInterface. Silent on missing/empty (no listeners is success).NoModelHooks(app/Models) — no model overridesboot()orbooted(). Silent on missing/empty.AutoloadFiles(composer.json) — fails ifautoload.filesorautoload-dev.filesis non-empty. Helpers files escape every layer; shared logic goes in a class under a layer.RouteHandler(routes/*.php) — eachRoute::*(verb)call must use a class-string handler (Foo::classor namespaced FQCN string). Closures, arrow functions, and[Controller::class, 'method']tuples fail.Route::redirect/Route::view/Route::permanentRedirect(no-handler forms) are allowed.
PHPStan rules (registered via extension.neon):
bounded.facadeZone— facades allowed only inframework_bridgezones (Providers, Middleware), not in logic. One whitelisted exception:Log::*.bounded.busChainBatchLiteral—Bus::chain/Bus::batchfirst arg must be a literal array at the call site.bounded.loggerEventKey— everyLog::*call must include'event' => stringin context.bounded.middlewareServiceImport— middleware cannot import fromapp/Services|Repositories|Queries|Integrations.bounded.noHttpTypesInServices— services/repositories cannot returnResponse/JsonResponse/View.bounded.noRequestInServiceSignatures— services cannot acceptIlluminate\Http\Requestin signatures.bounded.noInlineValidator—Validator::make(...)andvalidator($data, $rules)forbidden in logic / repository zones. Validation belongs in Form Requests.
Deptrac layer rules (shipped deptrac.yaml):
- Layer chain: Controllers → Services → Repositories/Queries → Models → Eloquent.
- Jobs may depend on Services only — not Models. Jobs are thin transport; model reads or mutations go through a service.
- Eloquent imports allowed only in Repositories / Queries / Models.
Coverage gate (arch:coverage:transactions):
- Every
Transaction::runcall site must have non-zero test coverage. Asserts via Clover report.
One commit boundary per use case — Transaction::run(callable) opens at most one DB transaction; nested calls throw. Compatible with Laravel's RefreshDatabase test trait — see Transaction service.
Webhook handlers
Webhooks use the same path as other endpoints: routes/api.php → invokable controller → Form Request (signature verified in authorize()) → Service. Webhook helper packages that introduce parallel controllers / jobs / models are rejected — they bypass the FormRequest layer.
Requirements
- PHP 8.3+
- Laravel 12 or 13
- For coverage gate:
pcovorxdebug
Install
composer require samaphp/laravel-bounded php artisan vendor:publish --tag=bounded-config
config/bounded.php is published — edit zones / ignore lists there. phpstan/phpstan, larastan/larastan, and deptrac/deptrac install transitively as runtime dependencies of the chain.
Configuration
config/bounded.php:
return [ 'zones' => [ 'logic' => ['app/Http/Controllers', 'app/Services', 'app/Integrations', 'app/Console/Commands', 'app/Jobs'], 'framework_bridge' => ['app/Providers', 'app/Http/Middleware'], 'repository' => ['app/Repositories', 'app/Queries'], ], 'ignore' => [ 'paths' => [ // 'app/Jobs', // skip all bounded checks for this category (e.g. mid-migration) ], ], ];
Zones partition. A path appears in exactly one zone. Boot fails (InvalidConfigurationException) on overlap.
Generators
php artisan make:action Order/Show # → app/Http/Controllers/Order/Show.php php artisan make:service Order/CreateOrder # → app/Services/Order/CreateOrder.php php artisan make:repository Order/OrderRepository # → app/Repositories/Order/OrderRepository.php php artisan make:integration Stripe/Gateway # → app/Integrations/Stripe/Gateway.php php artisan make:bounded-job Email/Send # → app/Jobs/Email/Send.php (named with `bounded-` prefix to avoid colliding with Laravel core's make:job)
Bare names without a Domain/ or Vendor/ segment are rejected. make:service CreateOrder errors out and prints the corrected form. Per-feature subfolders are mandatory for layers that hold use-case-shaped code; the generators reject paths the validators would reject anyway.
Validators (artisan)
php artisan arch:validate # run all validators, respect ignore.paths php artisan arch:validate --strict # bypass ignore.paths for file-level violations
ignore.paths declares "this category isn't applicable to my project." That declaration holds in strict mode for structural problems (ScanPathMissing / ScanPathEmpty from PathScanningValidator subclasses like SingleActionController). Strict only bypasses ignore for file-level violations — so a path can't be used to permanently silence broken code.
Full check chain
php artisan arch:check # arch:validate --strict → phpstan → deptrac → pest --coverage → coverage:transactions php artisan arch:check --skip-coverage # skip the transaction-coverage gate
In composer.json:
{
"scripts": {
"arch:check": "@php artisan arch:check"
}
}
PHPStan extension
In phpstan.neon:
includes: - vendor/samaphp/laravel-bounded/extension.neon
Rules registered via phpstan.rules.rule are listed under What it enforces.
Deptrac config
cp vendor/samaphp/laravel-bounded/deptrac.yaml deptrac.yaml vendor/bin/deptrac analyse
Layers and allowed dependencies are listed under What it enforces. Middleware boundaries are enforced by the bounded.middlewareServiceImport PHPStan rule, not Deptrac.
Transaction service
use Samaphp\LaravelBounded\Transaction\Transaction; final class CreateOrder { public function __construct(private readonly Transaction $transaction) {} public function execute(CreateOrderInput $input): Order { return $this->transaction->run(function () use ($input) { // persist + dispatch jobs explicitly return Order::create([...]); }); } }
Transaction::run(callable) opens a single DB transaction, runs the callback, commits on success, rolls back on exception. Nested calls throw TransactionAlreadyOpenException — one commit boundary per use case.
Compatible with RefreshDatabase. When App::runningUnitTests() is true and a transaction is already open, the existing one is treated as the test-fixture wrapper and the callback runs in a savepoint. The "no nesting" guard catches production programming errors and does not interfere with Laravel's test trait.
Coverage gate
vendor/bin/pest --coverage-clover=coverage.xml php artisan arch:coverage:transactions
The gate scans app/ for Transaction::run call sites, parses the Clover report, and asserts non-zero coverage on every call site. Fails with the list of uncovered lines if any. Requires pcov (recommended) or xdebug to generate the coverage report.
Detection convention. The gate matches two source patterns: Transaction::run( (static) and ->transaction->run( (instance via property). The instance pattern requires the property to be named exactly $transaction — if you inject the service as Transaction $tx and call $this->tx->run(...), the gate will miss it. Use $transaction as the property name.
Contributing / package dev
docker compose up -d docker compose exec app composer install docker compose exec app vendor/bin/pest --no-coverage # Pest 4 enforces coverage driver; --no-coverage works without pcov
Test matrix (multi-PHP)
docker compose --profile test run --rm test-php83 vendor/bin/pest --no-coverage docker compose --profile test run --rm test-php84 vendor/bin/pest --no-coverage docker compose --profile test run --rm test-php85 vendor/bin/pest --no-coverage
Support windows (verified 2026-05-02)
| Stack | Versions targeted | Source |
|---|---|---|
| PHP | 8.3 (security-only), 8.4 (active), 8.5 (active) | https://www.php.net/supported-versions.php |
| Laravel | 12 (active bug fixes), 13 (active bug fixes) | https://laravel.com/docs/releases |
PHP 8.2 excluded — Laravel 13 requires 8.3+, and 8.2 drops to EOL Dec 31, 2026. Laravel 11 excluded — security-only support ended Mar 12, 2026.
samaphp/laravel-bounded 适用场景与选型建议
samaphp/laravel-bounded 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 22 次下载、GitHub Stars 达 0, 最近一次更新时间为 2026 年 05 月 06 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「laravel」 「architecture」 「boundaries」 「PHPStan」 「deptrac」 「invokable-controllers」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 samaphp/laravel-bounded 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 samaphp/laravel-bounded 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 samaphp/laravel-bounded 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
Time range API for PHP
This package is a solution for easy building and working with Porto pattern structure
PHP tool to calculate architecture stability metrics based on clean architecture principles
Enforce architecture by defining modules with allowed dependencies. Detects forbidden, uncovered, missing and unused module dependencies in PHP projects.
Enterprise-Grade Modular Architecture for Laravel Applications - A powerful Laravel package that revolutionizes application development with robust Service Repository Pattern, Dynamic Module Management, and 50+ Artisan commands for scalable, maintainable applications.
A BJIT Reusable Laravel Package
统计信息
- 总下载量: 22
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 51
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-05-06