Arquitetura e padrões
Os sistemas Rails da Appolus seguem o MVC do Rails, mas com camadas extras para
manter controllers e models enxutos. O Syens (educacao) é a referência mais
completa dessas camadas.
Camadas do app/
Além de models, controllers e views, os projetos usam (conforme a
necessidade):
| Pasta | Responsabilidade |
|---|---|
services/ e actions/ | Regra de negócio orquestrada (service objects). Uma responsabilidade por classe. |
queries/ | Consultas complexas isoladas (query objects), em vez de scopes gigantes. |
serializers/ | Saída JSON das APIs (Active Model Serializers). |
presenters/ | Lógica de apresentação para as views. |
forms/ | Objetos de formulário (validação de entradas compostas). |
validators/ | Validações reutilizáveis. |
policies/ | Autorização (estilo Pundit). |
workers/ (ou jobs/) | Processamento assíncrono (Sidekiq). |
uploaders/ | Upload de arquivos. |
enumerations/, parsers/, adapters/ | Tipos de domínio, leitura de formatos externos, integrações. |
Controllers magros
O controller recebe a requisição, autoriza, delega para um service/model e responde. Nada de regra de negócio nele.
class EnrollmentsController < ApplicationController
def create
authorize Enrollment
result = Enrollments::Create.new(enrollment_params, school: current_school).call
if result.success?
redirect_to result.enrollment
else
render :new, locals: { errors: result.errors }
end
end
end
Service objects
Encapsulam uma operação de negócio, com entrada e saída claras. Facilitam teste e reuso.
module Enrollments
class Create
def initialize(attributes, school:)
@attributes = attributes
@school = school
end
def call
enrollment = @school.enrollments.new(@attributes)
enrollment.save
Result.new(success: enrollment.persisted?, enrollment: enrollment)
end
end
end
Query objects
Consultas pesadas ou reaproveitáveis ficam em objetos próprios, mantendo os models limpos e evitando SQL espalhado.
class ActiveStudentsQuery
def initialize(school:, school_year:)
@school = school
@school_year = school_year
end
def call
@school.students
.joins(:enrollments)
.where(enrollments: { school_year_id: @school_year.id, status: :active })
.distinct
end
end
Front-end
- No Syens, o padrão para telas novas é Bootstrap + ERB, com Vue.js apenas onde a interatividade justifica. Evite adicionar Backbone/jQuery novos.
- Na Chamada Escolar e no Relatórios, o front é React (empacotado com esbuild).
- O
cpe-inscricaoe o front do InfraSpawn são SPAs que consomem a API do respectivo backend.
Multitenancy (Syens)
O Syens atende várias escolas na mesma instância. Toda consulta e regra deve estar
escopada ao tenant (escola/cliente). Novos modelos com dados de escola precisam
de uma chave de tenant (school_id ou equivalente), validada e indexada. Nunca
escreva uma query que possa cruzar dados de escolas diferentes.