Skip to main content

Prezentare generală a arhitecturii

GlamB este construit folosind modelul Modular Monolith pe Rails 8.1+.
Domeniile sunt organizate în /app/ pe foldere; în viitor fiecare poate fi extras într-un Rails Engine separat.


Straturi

┌─────────────────────────────────────────────────────────┐
│ Presentation: REST API v1 · Hotwire · ActionCable │
├─────────────────────────────────────────────────────────┤
│ Application: Controllers · Services · Serializers │
│ Pundit Policies · Query Objects │
├─────────────────────────────────────────────────────────┤
│ Domain (11 engines — Phase 1: foldere în /app/): │
│ core · catalog · scheduling · booking · payments │
│ reviews · notifications · analytics · crm · admin │
│ favorites │
├─────────────────────────────────────────────────────────┤
│ Infrastructure: PostgreSQL 16 · Redis 7 · SolidQueue │
│ MinIO (S3) · ActionCable │
├─────────────────────────────────────────────────────────┤
│ External: Stripe · Twilio · SendGrid · Google Maps │
│ Firebase FCM · Cloudflare CDN │
└─────────────────────────────────────────────────────────┘

Structura directoarelor

app/
controllers/api/v1/
auth/ # sign_in, sign_up, refresh, google_oauth
me/ # profile, favorites, salons
salons/ # salons + toate resursele imbricate
appointments/ # appointments
catalogs/ # categories
invitations_controller.rb
base_controller.rb
services/
auth/ # AuthenticationService, GoogleOAuthService
me/ # ProfileService, FavoriteService
salons/ # SalonService, SalonServicesService, ...
appointments/ # AppointmentService
catalogs/ # CatalogService
models/ # User, Salon, Service, Appointment, ...
serializers/ # UserSerializer, SalonSerializer, ...
policies/ # SalonPolicy, AppointmentPolicy
lib/
jwt_signature.rb
domain_errors.rb
slot_builder.rb

Zeitwerk colapsează subdirectoarele services/ și serializers/ — clasele sunt declarate la nivel superior fără înveliș de modul.


Modelul serviciilor

Două modele în funcție de complexitate:

Pattern A — metode de clasă (operații simple fără stare):

class ProfileService
def self.get_profile(user_id)
profile = Profile.find_by(user_id: user_id)
raise DomainErrors::NotFound.new("Profile not found") unless profile
profile
end
end

Pattern B — instanță + #call (operații complexe cu metode private):

class SalonAvailabilityService
def call
# ...
end

private

def initialize(salon, date:, offering_id: nil)
@salon = salon
@date = Date.parse(date.to_s)
end
end

# Utilizare:
SalonAvailabilityService.new(salon, date: params[:date]).call

Autorizare

Pundit RBAC. Fiecare endpoint protejat apelează authorize sau policy_scope.

PoliticăResursă
SalonPolicySalon CRUD și gestionarea serviciilor
AppointmentPolicyProgramări cu acces bazat pe roluri
ApplicationPolicyClasă de bază, refuză tot implicit

Gestionarea erorilor

Toate erorile sunt centralizate în BaseController prin rescue_from:

ExcepțieHTTPCând
DomainErrors::NotFound404Resursa nu a fost găsită
DomainErrors::Unauthorized401Parolă / token invalid
DomainErrors::ValidationError422Validare model/parametri eșuată
DomainErrors::Forbidden403Permisiuni insuficiente
ActiveRecord::RecordNotFound404AR find
Pundit::NotAuthorizedError403Pundit
JwtSignature::TokenExpiredError401Token expirat
JwtSignature::TokenInvalidError401Token invalid