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.