The Metadata-Driven PHP Framework
Build enterprise-grade APIs by defining only your Domain. Everything else is automatic.
Metadata-driven. Convention-over-Configuration. Zero Boilerplate.
Stay updated on our progress toward v1.0.0. We are currently in active alpha development.
π View Full Roadmap on Wiki
BOUNDLY is a high-performance PHP framework that uses Metadata-Driven Architecture and Convention-over-Configuration. It eliminates boilerplate code by using PHP 8+ Attributes as the single source of truth for your infrastructure.
You define your Domain (entities, actions, business logic). BOUNDLY handles the Infrastructure (routes, controllers, validation, persistence).
- β Zero manual migrations β Your DB schema evolves with your code.
- β Zero route files β Your endpoints are declared on your Actions.
- β Zero boilerplate repositories β Generic CRUD is handled automatically.
- β Zero validation rules β Payloads are validated against your entity attributes.
- β Enterprise features in one line β Auditing, Soft Delete, Multi-Tenancy, Authorization, Rate Limiting.
- β Real-Time Agnostic β Propagate Domain Events to WebSockets without coupling your logic to a visual driver.
- β Production-ready β Static metadata cache, migration history, OpenAPI docs, and full CI/CD included.
BOUNDLY enforces a clean, screaming architecture where the folder structure tells you what the system does, not what framework it uses.
/
βββ Application/ # Use Cases (Actions, DTOs)
β βββ Users/
β βββ Actions/ # #[Action] defines the API endpoint
β βββ DTOs/
βββ Domain/ # Pure Business Logic
β βββ Users/
β βββ Entities/ # #[Entity] defines the DB table
β βββ Events/
β βββ ValueObjects/
βββ Infrastructure/ # Technical Adapters
β βββ FrameworkCore/ # The BOUNDLY Engine (CLI, Repos, Attributes)
β βββ LaravelEngine/ # Laravel internals (config, storage, routes...)
βββ bootstrap/ # Framework ignition point
βββ config/ # Your project config (boundly.php)
βββ public/ # Web entry point
βββ artisan # CLI entry point
Forget php artisan migrate. Define your entity, run the daemon, and your database evolves automatically.
#[Entity(table: 'users', resource: 'users')]
#[Auditable]
#[SoftDelete]
class User extends AggregateRoot
{
#[Id]
private int $id;
#[Column(type: 'string', length: 150)]
private string $name;
#[Column(type: 'string', nullable: true, default: '555-0000')]
private string $phone;
}Run php artisan core:watch and your /api/users endpoint is live. β¨
Protect any resource with a single attribute β no route middleware, no guards to register manually:
// Only admins and managers can access this resource
#[Entity(table: 'salaries', resource: 'salaries')]
#[Authorize(roles: ['admin', 'manager'])]
class Salary extends AggregateRoot { ... }
// Public reads, auth required for writes
#[Entity(table: 'articles', resource: 'articles')]
#[Authorize(roles: [], methods: ['POST', 'PUT', 'PATCH', 'DELETE'])]
class Article extends AggregateRoot { ... }Add enterprise features with a single attribute:
| Attribute | What it does |
|---|---|
#[Auditable] |
Injects created_by / updated_by β auto-populated from the request |
#[SoftDelete] |
Handles deleted_at and filters queries silently |
#[TenantAware] |
Multi-tenant data isolation at the repository level |
#[Authorize] |
Role-based access control β reads PHP Attributes, not config files |
#[Blameable] |
Extended audit trail tracking created_by/updated_by/deleted_by |
#[Timestampable] |
Auto-manage created_at/updated_at timestamps |
#[Sluggable] |
Auto-generate URL-friendly slugs from another field |
#[Policy] |
Map Laravel Policies for fine-grained authorization |
Protect sensitive data with declarative security:
| Attribute | What it does |
|---|---|
#[Hidden] |
Exclude properties from API responses |
#[Encrypted] |
Encrypt at rest with AES-256-CBC |
#[Hashed] |
One-way hashing (bcrypt/Argon2) |
#[RateLimit] |
API rate limiting with per-IP/user tracking |
Enterprise-grade security out of the box:
| Feature | Description |
|---|---|
| Input Sanitization | Whitelist approach - only declared columns are accepted |
| SQL Injection Prevention | Column whitelist validation in DynamicRepository |
| Rate Limiting | Built-in #[RateLimit] attribute |
| Authorization | #[Authorize] attribute with role-based access control |
Comprehensive data validation out of the box:
- Type: Email, Url, IpAddress, Uuid, Json, IsoDate, Timezone, ColorHex, Slug, MacAddress
- Numeric: Min, Max, Between, Positive, Negative, Integer, Decimal
- String: MinLength, MaxLength, LengthBetween, Alpha, Alphanumeric, Numeric, Pattern, StartsWith, EndsWith, Contains
- Format: Phone, CreditCard, PostalCode, Coordinates
- Database: Unique, Exists, Enum
- File: Image, Mimes, FileSize
- Compound: Required, Confirmed, Password, StrongPassword, SameAs, DifferentFrom
All relationship types supported:
| Relation | Attribute |
|---|---|
| One-to-Many | #[BelongsTo] / #[HasMany] |
| One-to-One | #[HasOne] |
| Many-to-Many | #[ManyToMany] (with pivot sync) |
| Polymorphic | #[MorphTo] / #[MorphMany] / #[MorphOne] |
Complex filtering, nested eager loading, and dual pagination out-of-the-box:
# Partial search
GET /api/users?name_like=boundly
# Range filtering with new operators
GET /api/users?age_gte=18&score_lte=100
# NOT and IN operators
GET /api/products?category_not=5&id_in=1,2,3
# OR filter groups
GET /api/users?or[name_like]=john&or[email_like]=john
# Nested eager loading (dot-notation)
GET /api/users?include=posts.comments.author
# Cursor pagination (efficient for large datasets)
GET /api/events?cursor=250&per_page=20
# Sorting & standard pagination
GET /api/users?sort=name&direction=asc&per_page=20Console output speaks your language:
php artisan core:watch --lang=esBroadcast your domain events to the frontend in real-time, completely decoupled from infrastructure (Reverb, Pusher, Soketi) using the purely semantic ShouldBroadcastToExterior contract.
Read the Integration Guide
composer create-project epolabs/boundly boundly --stability=alpha
cd my-projectOr clone the repository:
git clone https://github.com/EpOpenLabs/BOUNDLY.git my-project
cd my-project
composer installcp .env.example .env
php artisan key:generateConfigure your database in .env:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_DATABASE=my_project
DB_USERNAME=root
DB_PASSWORD=secret# .env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_DATABASE=my_project
DB_USERNAME=root
DB_PASSWORD=secret// Domain/Users/Entities/User.php
#[Entity(table: 'users', resource: 'users')]
#[Auditable]
class User extends AggregateRoot
{
#[Id]
private int $id;
#[Column(type: 'string', length: 150)]
private string $name;
#[Column(type: 'string', unique: true)]
private string $email;
}php artisan core:watchπ Your API is live at http://localhost:8000/api/users.
# Preview schema changes
php artisan core:migrate --dry-run
# Apply schema
php artisan core:migrate
# Cache metadata for zero-overhead boot
php artisan core:cache
# Generate OpenAPI documentation
php artisan core:docs| Key | Default | Env Variable | Description |
|---|---|---|---|
locale |
en |
BOUNDLY_LOCALE |
Default language for CLI output |
api_prefix |
api |
BOUNDLY_API_PREFIX |
URL prefix for all auto-generated routes |
disable_cache |
true in local |
BOUNDLY_DISABLE_CACHE |
Forces scan mode; set false in production |
auth.default_guard |
sanctum |
BOUNDLY_AUTH_GUARD |
Guard used by #[Authorize] middleware |
pagination.default_per_page |
15 |
BOUNDLY_PER_PAGE |
Default page size |
pagination.max_per_page |
100 |
BOUNDLY_MAX_PER_PAGE |
Hard cap on page size |
rate_limit.enabled |
true |
BOUNDLY_RATE_LIMIT_ENABLED |
Enable/disable rate limiting |
rate_limit.max_attempts |
60 |
BOUNDLY_RATE_LIMIT_MAX_ATTEMPTS |
Max requests per window |
rate_limit.decay_minutes |
1 |
BOUNDLY_RATE_LIMIT_DECAY_MINUTES |
Time window in minutes |
paths.domain |
Domain/ |
β | Where BOUNDLY scans for #[Entity] |
paths.application |
Application/ |
β | Where BOUNDLY scans for #[Action] |
- PHP 8.2+
- Laravel 13+ (core dependency, hidden in
Infrastructure/LaravelEngine) - MySQL / PostgreSQL / SQLite
BOUNDLY is an open source project and contributions are welcome!
Please read CONTRIBUTING.md for details on:
- How to report bugs
- How to propose new features
- The Pull Request process
- Code style guidelines
BOUNDLY follows Semantic Versioning (SemVer):
MAJORβ Breaking changes in the API or architectureMINORβ New backward-compatible featuresPATCHβ Bug fixes
See the full history in CHANGELOG.md.
BOUNDLY is open-sourced software licensed under the MIT License.
If BOUNDLY has been useful to you or you like what we're building, you can support us to keep creating and maintaining open source software:
A coffee = more time for open source code β€οΈ
β If you like BOUNDLY, give it a star on GitHub! β
GitHub Β· Issues Β· Discussions
Made with β€οΈ by EpOpenLabs