AbstractService
v1.1.0The 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:
class UserService extends AbstractService
{
public function __construct(UserRepository $repository)
{
parent::__construct($repository);
}
}Service Lifecycle
Every generated operation follows the same execution flow.
The Service acts as the orchestration layer between HTTP and persistence.
Protected Properties
$repository
protected AbstractRepository $repository;Stores the injected Repository instance.
Every CRUD operation eventually reaches the Repository through this property.
Example:
public function __construct(UserRepository $repository)
{
parent::__construct($repository);
}This guarantees that every generated Service works with a strongly typed Repository.
Constructor
public function __construct(AbstractRepository $repository)The constructor registers the Repository used by the Service.
Execution:
- Repository is injected.
- Parent constructor stores it.
- CRUD methods become available immediately.
Example:
public function __construct(UserRepository $repository)
{
parent::__construct($repository);
}Core CRUD Methods
create()
Creates a new record using a DTO.
Example:
$user = $service->create($dto);Execution flow:
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:
$service->update($publicId, $dto);Flow:
- Resolve public identifier.
- Apply business rules.
- Persist changes.
delete()
Deletes a record.
When the Model uses SoftDeletes:
use SoftDeletes;the generated Service preserves that behavior automatically.
find()
Retrieves one entity.
Example:
$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:
$user = $service->findOrFail($publicId);If the entity does not exist, the Repository throws a normalized exception.
paginate()
Returns paginated data.
Example:
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:
GET /api/users/01JXYZABCDEF123456The Service delegates resolution to the Repository.
Transactions
One of the main responsibilities of the Service layer is transaction management.
Example:
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:
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:
Controller
↓
Service
↓
Repository
↓
DatabaseExample:
$this->repository->create($dto->toArray());This keeps persistence replaceable.
Relationship Loading
The Service can request eager-loaded relationships through Repository methods.
Example:
$this->repository->with([
'municipio'
]);Benefits:
- fewer queries
- predictable API responses
Pagination Flow
Pagination follows the same pipeline every time.
Controller
↓
Service
↓
Repository::paginate()
↓
Paginator
↓
Resource CollectionThe Controller never builds pagination manually.
Error Handling
Business exceptions remain inside the Service layer.
Example:
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.