Skip to content

AbstractService ​

v1.1.0

The AbstractService is the business layer of Laravel Domain Generator.

Controllers never contain business rules. Instead, every operation is delegated to a Service, which coordinates validation results, DTOs, repositories and transactions.

Every Service generated by php artisan make:domain extends this class automatically.


Overview ​

Generated Services provide a consistent place for business logic while keeping Controllers extremely small.

Responsibilities include:

  • Business rules
  • Repository orchestration
  • DTO processing
  • Database transactions
  • Public identifier support
  • Pagination
  • Relationship loading
  • Reusable domain operations

Minimal example:

php
class UserService extends AbstractService
{
    public function __construct(UserRepository $repository)
    {
        parent::__construct($repository);
    }
}

Service Lifecycle ​

Every generated operation follows the same execution flow.

ControllerDTOServiceRepositoryModel

The Service acts as the orchestration layer between HTTP and persistence.


Protected Properties ​

$repository ​

php
protected AbstractRepository $repository;

Stores the injected Repository instance.

Every CRUD operation eventually reaches the Repository through this property.

Example:

php
public function __construct(UserRepository $repository)
{
    parent::__construct($repository);
}

This guarantees that every generated Service works with a strongly typed Repository.


Constructor ​

php
public function __construct(AbstractRepository $repository)

The constructor registers the Repository used by the Service.

Execution:

  1. Repository is injected.
  2. Parent constructor stores it.
  3. CRUD methods become available immediately.

Example:

php
public function __construct(UserRepository $repository)
{
    parent::__construct($repository);
}

Core CRUD Methods ​

create() ​

Creates a new record using a DTO.

Example:

php
$user = $service->create($dto);

Execution flow:

text
DTO
 ↓
Service
 ↓
Repository
 ↓
Model::create()

Typical responsibilities inside create():

  • validate business rules
  • execute transactions
  • delegate persistence

Never receive raw HTTP Requests here.


update() ​

Updates an existing record.

Example:

php
$service->update($publicId, $dto);

Flow:

  1. Resolve public identifier.
  2. Apply business rules.
  3. Persist changes.

delete() ​

Deletes a record.

When the Model uses SoftDeletes:

php
use SoftDeletes;

the generated Service preserves that behavior automatically.


find() ​

Retrieves one entity.

Example:

php
$user = $service->find($publicId);

Instead of exposing internal IDs, Services work with public identifiers whenever possible.


findOrFail() ​

Works like Laravel's findOrFail() but keeps Repository responsibilities centralized.

Example:

php
$user = $service->findOrFail($publicId);

If the entity does not exist, the Repository throws a normalized exception.


paginate() ​

Returns paginated data.

Example:

php
return $service->paginate();

The Service delegates pagination while keeping Controllers unaware of Repository implementation details.

Response automatically includes:

  • data
  • links
  • meta

Public Identifier Support ​

Generated Services work with public identifiers instead of exposing database IDs.

Supported identifiers include:

  • ULID
  • UUID
  • UUID32
  • custom hash identifiers

Example endpoint:

text
GET /api/users/01JXYZABCDEF123456

The Service delegates resolution to the Repository.


Transactions ​

One of the main responsibilities of the Service layer is transaction management.

Example:

php
DB::transaction(function () use ($dto) {
    $this->repository->create($dto->toArray());
});

Benefits:

  • atomic operations
  • rollback on failure
  • safer business rules

Whenever multiple writes happen together, they belong here.


Business Rules ​

Services are the correct place for domain rules.

Example:

php
if (! $user->ativo) {
    throw new DomainException();
}

Avoid placing these rules inside:

  • Controllers
  • Repositories
  • Resources

Keeping them here makes the domain reusable.


Repository Delegation ​

The Service never performs database operations directly.

Instead:

text
Controller
 ↓
Service
 ↓
Repository
 ↓
Database

Example:

php
$this->repository->create($dto->toArray());

This keeps persistence replaceable.


Relationship Loading ​

The Service can request eager-loaded relationships through Repository methods.

Example:

php
$this->repository->with([
    'municipio'
]);

Benefits:

  • fewer queries
  • predictable API responses

Pagination Flow ​

Pagination follows the same pipeline every time.

text
Controller
 ↓
Service
 ↓
Repository::paginate()
 ↓
Paginator
 ↓
Resource Collection

The Controller never builds pagination manually.


Error Handling ​

Business exceptions remain inside the Service layer.

Example:

php
throw new DomainException(
    'Inactive users cannot perform this operation.'
);

The Controller later transforms this into a standardized JSON response.


Best Practices ​

Keep Services focused on business logic.

Recommended:

  • receive DTOs
  • call Repositories
  • execute transactions
  • validate domain rules

Avoid:

  • receiving Requests
  • returning HTTP Responses
  • querying Models directly

The generated Service keeps business rules isolated from both HTTP and persistence, making your domain easier to test and maintain.

Released under the MIT License.