Desenvolvendo um Sistema em Laravel — Parte 2: Arquitetura Multiempresa

Introdução

Na Parte 1, estruturamos todo o ambiente de desenvolvimento e colocamos um projeto Laravel 12 funcional de pé.

Nesta segunda etapa, entramos no coração do sistema: a arquitetura de dados, preparando a aplicação para operar como um sistema multiempresa, com controle de usuários e papéis bem definidos.

Essa fase é fundamental, pois decisões erradas aqui geram dívida técnica quase impossível de eliminar no futuro.


Objetivo da Arquitetura

O sistema deve:

  • suportar múltiplas empresas no mesmo código;
  • garantir isolamento total de dados;
  • permitir controle de acesso por papéis (roles);
  • manter o modelo de domínio simples, claro e extensível.

Mas antes vamos conceituar os que usaremos nessa etapa:

O que são Models no Laravel

Os Models representam as entidades principais do sistema e são responsáveis por encapsular a lógica de negócio e o relacionamento com o banco de dados. Cada model corresponde, em geral, a uma tabela e permite que a aplicação trabalhe com dados de forma orientada a objetos, sem escrever SQL diretamente na maior parte do tempo. Ao utilizar models, o código se torna mais legível, mais seguro e muito mais fácil de manter, pois toda a lógica relacionada àquela entidade fica concentrada em um único ponto.

Além disso, os models permitem definir relacionamentos entre entidades (como usuários pertencendo a uma empresa ou possuindo um papel), o que torna as consultas muito mais expressivas e alinhadas com o domínio do problema. Em um sistema estruturado, o model deixa de ser apenas um espelho da tabela e passa a ser o verdadeiro coração do comportamento da aplicação.


O que são Migrations

As migrations funcionam como o histórico de evolução do banco de dados. Cada migration descreve uma alteração estrutural: criação de tabelas, inclusão ou remoção de colunas, criação de índices e relacionamentos. Em vez de editar o banco manualmente, o desenvolvedor registra cada mudança em código, permitindo que o banco seja reproduzido de forma idêntica em qualquer ambiente apenas executando os comandos do Laravel.

Esse mecanismo garante rastreabilidade, controle de versão e consistência entre ambientes de desenvolvimento, homologação e produção. Em projetos reais, migrations são essenciais para manter o banco sincronizado com a aplicação ao longo do tempo, eliminando erros humanos e facilitando a colaboração entre desenvolvedores.


O que são Seeders

Os seeders são responsáveis por popular o banco de dados com informações iniciais ou de teste. Eles permitem criar dados essenciais para que o sistema funcione desde o primeiro acesso, como usuários administradores, empresas iniciais e configurações básicas, sem depender de inserções manuais no banco.

Em projetos bem estruturados, os seeders transformam um banco vazio em um sistema pronto para uso com um único comando. Isso acelera o desenvolvimento, reduz erros de configuração e garante que todos os ambientes iniciem sempre no mesmo estado controlado, o que é fundamental para projetos profissionais e de longo prazo.


Entendidos os conceitos, vamos então ver os passos desta parte:

Criação do Modelo de Empresas

Criamos o model e migration da entidade companies:

php artisan make:model Company -m

Estrutura da tabela:

Schema::create('companies', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->string('cnpj', 14)->unique();
    $table->string('slug')->unique();
    $table->boolean('active')->default(true);
    $table->timestamps();
});

Criação do Modelo de Papéis (Roles)

Em seguida, criamos a entidade roles:

php artisan make:model Role -m
Schema::create('roles', function (Blueprint $table) {
    $table->id();
    $table->string('name')->unique();
    $table->string('description')->nullable();
    $table->timestamps();
});

Extensão da Tabela de Usuários

A tabela users foi estendida para suportar a arquitetura multiempresa:

php artisan make:migration add_company_and_role_to_users_table
Schema::table('users', function (Blueprint $table) {
    $table->foreignId('company_id')->nullable()->constrained();
    $table->foreignId('role_id')->nullable()->constrained();
    $table->boolean('active')->default(true);
});

Relacionamentos de Domínio

Os models foram conectados com relacionamentos claros:

User

public function company()
{
    return $this->belongsTo(Company::class);
}

public function role()
{
    return $this->belongsTo(Role::class);
}

Company

public function users()
{
    return $this->hasMany(User::class);
}

Role

public function users()
{
    return $this->hasMany(User::class);
}

Seeders Iniciais

Criamos dados mínimos para o sistema funcionar desde o primeiro acesso.

Seeder de Roles

php artisan make:seeder RoleSeeder
foreach ($roles as $role) {
    Role::firstOrCreate(
        ['name' => $role['name']],
        ['description' => $role['description']]
    );
}

Seeder de Empresa e Usuário Inicial

php artisan make:seeder InitialSetupSeeder
$company = Company::create([...]);

$role = Role::where('name', 'super_admin')->firstOrFail();

User::create([
    'name' => 'Administrador',
    'email' => 'admin@meusistema.com',
    'password' => Hash::make('password'),
    'company_id' => $company->id,
    'role_id' => $role->id,
    'active' => true,
]);

Registro no DatabaseSeeder:

$this->call([
    RoleSeeder::class,
    InitialSetupSeeder::class,
]);

Execução:

php artisan migrate:fresh
php artisan db:seed

Resultado da Parte 2

Ao final desta etapa, o sistema já possui:

  • estrutura multiempresa funcional;
  • controle de usuários por papéis;
  • isolamento de dados por empresa;
  • dados iniciais prontos para operação.

Essa base permite evoluir o projeto com segurança, previsibilidade e organização.

Leia também Desenvolvendo um Sistema em Laravel — Parte 1: Stack e Ambiente Inicial.