Pular para o conteúdo principal

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):

PastaResponsabilidade
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-inscricao e 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.