Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/adhenrique/laravel-domain-oriented
Build a domain-oriented Api on Laravel Framework
https://github.com/adhenrique/laravel-domain-oriented
ddd domain domain-builder domain-driven-design domain-oriented laravel laravel-framework laravel-library laravel-package
Last synced: about 1 month ago
JSON representation
Build a domain-oriented Api on Laravel Framework
- Host: GitHub
- URL: https://github.com/adhenrique/laravel-domain-oriented
- Owner: adhenrique
- License: mit
- Created: 2020-12-28T03:57:05.000Z (almost 4 years ago)
- Default Branch: master
- Last Pushed: 2022-01-08T19:40:15.000Z (almost 3 years ago)
- Last Synced: 2024-09-30T10:40:55.647Z (about 1 month ago)
- Topics: ddd, domain, domain-builder, domain-driven-design, domain-oriented, laravel, laravel-framework, laravel-library, laravel-package
- Language: PHP
- Homepage:
- Size: 124 KB
- Stars: 11
- Watchers: 2
- Forks: 2
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE.md
Awesome Lists containing this project
README
# Laravel Domain Oriented
This package builds a structure to domain-oriented APIs (not DDD, they are different things). With search filters, validations and clean code.
## Requirements
- PHP 7.2+, 8.0 (new version)
- Laravel 7.x, 8 (prefer-stable)## Introduction
My need was simple: build structures in an organized and productive way. A structure that supports filters, validations and data caching (CQRS).Before proceeding, take a look at the final structure:
```bash
app
├── ...
├── Domain
│ └── Dummy
│ ├── DummyFilterService.php
│ ├── DummyPersistenceModel.php
│ ├── DummyPersistenceService.php
│ ├── DummyPolicy.php
│ ├── DummyResource.php
│ ├── DummySearchModel.php
│ ├── DummySearchService.php
│ └── DummyValidateService.php
├── Http
│ ├── Controllers
│ │ ├── ...
│ │ └── DummyController.php
├── ...
database
├── factories
│ └── ...
│ └── DummyFactory.php
├── migrations
│ ├── ...
│ └── 2021_01_06_193044_create_dummies_table.php
└── seeders
├── DatabaseSeeder.php
└── DummySeeder.php
```You must be asking yourself:
1. Why not use Repository Pattern?
A. It is not possible to obtain a database abstraction more than what Eloquent offers. [1](https://adelf.tech/2019/useless-eloquent-repositories "Please, stop talking about Repository pattern with Eloquent")
2. What is the idea of PersistenceModel, and SearchModel?
A. In fact, I go further. Model instances of Eloquent should not be returned. So we guarantee a "read-only" instance (which is not used for persistence in the database) [2](https://adelf.tech/2019/read-eloquent-repositories "Useful Eloquent Repositories?") [3](https://medium.com/laraveltips/voc%C3%AA-entende-repository-pattern-voc%C3%AA-est%C3%A1-certo-disso-d739ecaf544e "Você entende Repository Pattern? Você está certo disso?")
3. There are a lot of files, how do I build it all?
A. It's simple, get a coffee and let's do it...
## Setup
1. Run this Composer command to install the latest version
```bash
$ composer require adhenrique/laravel-domain-oriented
```
2. If you prefer, you can export the location files:
```bash
php artisan vendor:publish --provider="LaravelDomainOriented\ServiceProvider" --tag="lang"
```
3. Run this command to build the domain structure:
```bash
$ php artisan domain:create Dummy
```
4. Stay calm. If the structure already exists, the console asks you if you want to rewrite it, unless you pass the `--force` flag:
```bash
$ php artisan domain:create Dummy --force
```
5. And of course, if you want to remove the structure, just run this command:
```bash
$ php artisan domain:remove Dummy
```
That's it enjoy!## Configuration
### Adjust your Models
Our Model's follow the [Eloquent Model Conventions](https://laravel.com/docs/8.x/eloquent#eloquent-model-conventions)
- PersistenceModel: used only for persistence in the database. Define your fields, casts, etc...
- SearchModel: used for searches. It is very likely that your [relationship](https://laravel.com/docs/8.x/eloquent-relationships) will be here.### Adjust your Migrations
Our Migrations follow the [Laravel Migration Structure](https://laravel.com/docs/8.x/migrations#migration-structure)### Adjust your Seeders and Factories
Here, too, we follow the Laravel way of doing things:
- [Factories](https://laravel.com/docs/8.x/database-testing)
- [Seeders](https://laravel.com/docs/8.x/seeding)### Adjust your Policy
Again, Policies follow the [Laravel Policy Authorization](https://laravel.com/docs/8.x/authorization#writing-policies)
> Note: You don't have to worry about registering your policies, as we do it behind the scenes. However, here we follow a class name convention. When creating a domain, your class must be named SomethingPolicy and belong to the App\Domain\Something namespace.### Config your validations
ValidateService is located at `app/Domain/YourDomainName/*`:
```php
use LaravelDomainOriented\Services\ValidateService;class DummyValidateService extends ValidateService
{
protected array $rules = [
// You can define general validation rules, which will be inherited
// for all actions, or you can define validation rules for each action:
// SHOW, STORE, UPDATE, DESTROY// General rules validation.
// If any action validation rule is not defined, it will inherit from here.
'name' => 'required|string',// Specific action rules validation. If set, ignores general validations.
self::SHOW => [
'id' => 'required|integer',
],
self::UPDATE => [
'id' => 'required|integer',
'name' => 'required|string',
],
self::DESTROY => [
'id' => 'required|integer',
],
];
}
```
### Config routes
We follow [Laravel routes](https://laravel.com/docs/8.x/routing) pattern. But as we are dealing with API, modify the file `routes/api.php`, adding the following routes:
```php
Route::get('dummies', 'App\Http\Controllers\DummyController@index');
Route::get('dummies/{id}', 'App\Http\Controllers\DummyController@show');
Route::post('dummies', 'App\Http\Controllers\DummyController@store');
Route::put('dummies/{id}', 'App\Http\Controllers\DummyController@update');
Route::delete('dummies/{id}', 'App\Http\Controllers\DummyController@destroy');
```## Using
### Before Search filters
In the SearchService class you have two methods that help you to pre-start queries according to your needs: `beforeAll` and` beforeFindById`.
Each method receives 2 parameters: `builder` with the Eloquent instance started and `auth`, with the user session - if are logged in.
You just need to override the methods, but ensure that the return is eloquent's `Builder`. Look:
```php
class DummySearchService extends SearchService
{
protected SearchModel $model;
protected FilterService $filterService;public function __construct(DummySearchModel $model, DummyFilterService $filterService)
{
$this->model = $model;
$this->filterService = $filterService;
}public function beforeAll(Builder $builder, Guard $auth): Builder
{
return $builder;
}public function beforeFindById(Builder $builder, Guard $auth): Builder
{
return $builder;
}
}
```
In my use case, logged in as admin, I usually filter from the list of users my own user. Look:
```php
// ...
public function beforeAll(Builder $builder, Guard $auth): Builder
{
return $this->removeLoggedFromSearches($builder, $auth);
}private function removeLoggedFromSearches($builder, $auth)
{
$id = $auth->id();
return $builder->where('id', '<>', $id);
}
```### Searching with filters
You can filter and paginate the data on the listing routes. To do this, send a payload on the request, using your favorite client:**Simple Where:**
```json
{
"name": "adhenrique",
"email": "[email protected]"
}
```**Where in:**
```json
{
"id": [1,2,3]
}
```**Where by operator (like, >, =>, <, <=, <>):**
```json
{
"name": {
"operator": "like",
"value": "%adhenrique%"
}
}
```**Where between:**
```json
{
"birthdate": {
"start": "1988-13-12",
"end": "2021-01-01"
}
}
```**Paginate results**
```json
{
"paginate": {
"per_page": 1,
"page": 1
}
}
```
**Note:** You can use the filters and pagination together.## Todo
- [ ] CQRS
- [ ] Support for old Laravel versions
- [ ] Or Where filter
- [ ] OOP improvements
- [ ] Add beforeAll and beforeFindById tests
- [ ] Ask to confirm name
- [ ] Add way to test Policies## Testing
```bash
$ composer test
```### Changelog
Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently.
## Contributing
Please see [CONTRIBUTING](CONTRIBUTING.md) for details.
## Security
If you discover any security related issues, please email [[email protected]](mailto:[email protected]) instead of using the issue tracker.## Credits
- [Bruno Maranesi](https://github.com/maranesi)
- [Adelf](https://adelf.tech/)
- [Brent](https://stitcher.io/)## License
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
## Reading Articles
[1] [Please, stop talking about Repository pattern with Eloquent](https://adelf.tech/2019/useless-eloquent-repositories)
[2] [Useful Eloquent Repositories?](https://adelf.tech/2019/read-eloquent-repositories)
[3] [Você entende Repository Pattern? Você está certo disso?](https://medium.com/laraveltips/voc%C3%AA-entende-repository-pattern-voc%C3%AA-est%C3%A1-certo-disso-d739ecaf544e)
[Laravel — Why you’ve been using the Repository Pattern the wrong way](https://medium.com/@sergiumneagu/laravel-why-youve-been-using-the-repository-pattern-the-wrong-way-952aedf1989b)