DB Schema — GRegistry / Registrul Sistemelor Informaționale (RSI)
Schema derivată din GRegistry.jdl (JDL v2) pentru verificare manuală înainte
de jhipster jdl GRegistry.jdl.
Tipuri de date: PostgreSQL (profil prod și dev — GRegistry folosește
PostgreSQL pe ambele). Package md.gov.gregistry, port 8085, autentificare
OAuth2 / OIDC (GSSO / Keycloak). Toate tabelele mutabile moștenesc 4 câmpuri de
audit de la AbstractAuditingEntity (omise din unele DDL-uri pentru lizibilitate
— vezi §6).
Fără JSONB, fără Elasticsearch, fără Kafka în acest sistem — spre deosebire de
Registrul Interdicțiilor, GRegistry nu are coloane TextBlob/jsonb de convertit
(vezi §7). Registrul e un catalog structurat, read-heavy pentru consumatori
(GPortal, GLog, GStorage, GMonitor).
Documente conexe: platform_design.md (arhitectură, diagrame de flux),
caiet_de_sarcini.md (cerințe funcționale), Home.md (wiki / glosar).
1. ER diagram
Section titled “1. ER diagram”erDiagram PROVIDER ||--o{ INFORMATION_SYSTEM : "deține" INFORMATION_SYSTEM ||--o{ ENVIRONMENT : "rulează în" INFORMATION_SYSTEM ||--o{ PUBLICATION_REQUEST : "publicat prin" INFORMATION_SYSTEM ||--o{ API_ENDPOINT : "expune" INFORMATION_SYSTEM ||--o{ REGISTRY_AUDIT : "audit append-only" INFORMATION_SYSTEM ||--o{ SYSTEM_DEPENDENCY : "consumator (system)" INFORMATION_SYSTEM ||--o{ SYSTEM_DEPENDENCY : "furnizor (dependsOn)"
PROVIDER { bigint id PK varchar code "60 UK" varchar name "160" varchar legal_name "240" varchar contact_email "160" varchar website "240" boolean active } INFORMATION_SYSTEM { bigint id PK varchar code "80 UK - ^[a-z][a-z0-9-]*$" varchar name "160" varchar category "120" varchar system_type "enum SystemType (4)" varchar status "enum SystemStatus (5)" varchar visibility "enum Visibility (3)" varchar criticality "enum Criticality (4)" varchar security_level "enum SecurityLevel (3)" boolean handles_personal_data varchar current_version "40" varchar description_ro "2000" varchar description_en "2000" varchar tech_lead "160" varchar tech_lead_email "160" boolean published_to_portal timestamptz created_at timestamptz updated_at bigint provider_id FK } ENVIRONMENT { bigint id PK varchar environment_type "enum EnvironmentType (4)" varchar state "enum EnvironmentState (5)" varchar endpoint_url "300" varchar deployed_version "40" boolean monitored timestamptz last_checked_at bigint system_id FK } SYSTEM_DEPENDENCY { bigint id PK varchar kind "enum DependencyKind (5)" boolean required varchar note "500" bigint system_id FK "consumator" bigint depends_on_id FK "furnizor" } PUBLICATION_REQUEST { bigint id PK varchar state "enum PublicationState (6)" varchar requested_by "160" timestamptz requested_at varchar approved_by "160" timestamptz approved_at timestamptz published_at varchar requested_visibility "enum Visibility (3)" varchar comment "1000" bigint system_id FK } API_ENDPOINT { bigint id PK varchar path "300" varchar protocol "enum ApiProtocol (5)" varchar method "16" varchar description "500" boolean public_api bigint system_id FK } REGISTRY_CONSUMER { bigint id PK varchar name "160" varchar description "500" varchar filter_expression "200" boolean active } REGISTRY_AUDIT { bigint id PK varchar change_type "enum ChangeType (8)" varchar actor "160" varchar summary_ro "500" varchar summary_en "500" varchar ip_address "64" timestamptz occurred_at bigint system_id FK "opțional" }
REGISTRY_CONSUMERnu are relații în JDL — este un catalog de sine stătător al consumatorilor de registru.SYSTEM_DEPENDENCYeste o entitate-join care materializează un graf de dependențe direcționat auto-referențial pesteINFORMATION_SYSTEM(system → dependsOn).
2. Tabele de domeniu (DDL)
Section titled “2. Tabele de domeniu (DDL)”2.1 provider — instituția/organizația care deține sisteme
Section titled “2.1 provider — instituția/organizația care deține sisteme”CREATE TABLE provider ( id BIGINT NOT NULL PRIMARY KEY, code VARCHAR(60) NOT NULL, name VARCHAR(160) NOT NULL, legal_name VARCHAR(240), contact_email VARCHAR(160), website VARCHAR(240), active BOOLEAN NOT NULL, -- audit (§6) created_by VARCHAR(50) NOT NULL, created_date TIMESTAMPTZ, last_modified_by VARCHAR(50), last_modified_date TIMESTAMPTZ, CONSTRAINT ux_provider_code UNIQUE (code) -- code: minlength(2) maxlength(60) — min impus la nivel de aplicație (Bean Validation) -- contact_email: pattern e-mail — impus la nivel de aplicație);
CREATE INDEX ix_provider_active ON provider (active);2.2 information_system — nucleul registrului (RSI)
Section titled “2.2 information_system — nucleul registrului (RSI)”CREATE TABLE information_system ( id BIGINT NOT NULL PRIMARY KEY, code VARCHAR(80) NOT NULL, -- ^[a-z][a-z0-9-]*$ (aplicație) name VARCHAR(160) NOT NULL, category VARCHAR(120), system_type VARCHAR(32) NOT NULL, status VARCHAR(32) NOT NULL, visibility VARCHAR(32) NOT NULL, criticality VARCHAR(32) NOT NULL, security_level VARCHAR(32) NOT NULL, handles_personal_data BOOLEAN NOT NULL, current_version VARCHAR(40), description_ro VARCHAR(2000), description_en VARCHAR(2000), tech_lead VARCHAR(160), tech_lead_email VARCHAR(160), published_to_portal BOOLEAN NOT NULL, created_at TIMESTAMPTZ, updated_at TIMESTAMPTZ,
provider_id BIGINT NOT NULL, -- ManyToOne required
-- audit (§6) created_by VARCHAR(50) NOT NULL, created_date TIMESTAMPTZ, last_modified_by VARCHAR(50), last_modified_date TIMESTAMPTZ,
CONSTRAINT fk_is_provider FOREIGN KEY (provider_id) REFERENCES provider(id), CONSTRAINT ux_is_code UNIQUE (code), CONSTRAINT chk_is_system_type CHECK (system_type IN ('CORE','BUSINESS','PAAS','INFRASTRUCTURE')), CONSTRAINT chk_is_status CHECK (status IN ('PLANNED','DEMO','ACTIVE','DEPRECATED','RETIRED')), CONSTRAINT chk_is_visibility CHECK (visibility IN ('PUBLIC','INTERNAL','TEST')), CONSTRAINT chk_is_criticality CHECK (criticality IN ('CRITICAL','IMPORTANT','NORMAL','LOW')), CONSTRAINT chk_is_security_level CHECK (security_level IN ('HIGH','MEDIUM','LOW')));
CREATE INDEX ix_is_provider ON information_system (provider_id);CREATE INDEX ix_is_system_type ON information_system (system_type);CREATE INDEX ix_is_status ON information_system (status);-- Calea de citire publică/consumator: GPortal cere sistemele PUBLIC publicate.CREATE INDEX ix_is_visibility_published ON information_system (visibility, published_to_portal);
created_at/updated_at(câmpuri de domeniu din JDL) coexistă cucreated_date/last_modified_date(auditAbstractAuditingEntity). Cele din JDL sunt setate de logica aplicativă a registrului; cele de audit, automat. Vezi §8 — de decis dacă se elimină duplicarea.
2.3 environment — mediu de deployment (prod/staging/test/dev)
Section titled “2.3 environment — mediu de deployment (prod/staging/test/dev)”CREATE TABLE environment ( id BIGINT NOT NULL PRIMARY KEY, environment_type VARCHAR(32) NOT NULL, state VARCHAR(32) NOT NULL, endpoint_url VARCHAR(300), deployed_version VARCHAR(40), monitored BOOLEAN NOT NULL, last_checked_at TIMESTAMPTZ, system_id BIGINT NOT NULL, -- OneToMany invers, required -- audit (§6) created_by VARCHAR(50) NOT NULL, created_date TIMESTAMPTZ, last_modified_by VARCHAR(50), last_modified_date TIMESTAMPTZ, CONSTRAINT fk_env_system FOREIGN KEY (system_id) REFERENCES information_system(id), CONSTRAINT chk_env_type CHECK (environment_type IN ('PRODUCTION','STAGING','TEST','DEV')), CONSTRAINT chk_env_state CHECK (state IN ('UP','DEGRADED','DOWN','PLANNED','NONE')));
CREATE INDEX ix_env_system ON environment (system_id);CREATE INDEX ix_env_state ON environment (state);2.4 system_dependency — graf de dependențe direcționat (join auto-referențial)
Section titled “2.4 system_dependency — graf de dependențe direcționat (join auto-referențial)”CREATE TABLE system_dependency ( id BIGINT NOT NULL PRIMARY KEY, kind VARCHAR(32) NOT NULL, required BOOLEAN NOT NULL, note VARCHAR(500), system_id BIGINT NOT NULL, -- consumatorul (system) depends_on_id BIGINT NOT NULL, -- serviciul consumat (dependsOn) -- audit (§6) created_by VARCHAR(50) NOT NULL, created_date TIMESTAMPTZ, last_modified_by VARCHAR(50), last_modified_date TIMESTAMPTZ, CONSTRAINT fk_dep_system FOREIGN KEY (system_id) REFERENCES information_system(id), CONSTRAINT fk_dep_depends_on FOREIGN KEY (depends_on_id) REFERENCES information_system(id), CONSTRAINT chk_dep_kind CHECK (kind IN ('API','EVENT','DATA','AUTH','STORAGE')), -- un sistem nu depinde de sine CONSTRAINT chk_dep_no_self CHECK (system_id <> depends_on_id));
-- o pereche (consumator, furnizor) apare o singură dată per tip de dependențăCREATE UNIQUE INDEX ux_dep_edge ON system_dependency (system_id, depends_on_id, kind);CREATE INDEX ix_dep_system ON system_dependency (system_id, depends_on_id);CREATE INDEX ix_dep_depends_on ON system_dependency (depends_on_id);Coloana
requiredeste un cuvânt permis ca identificator în PostgreSQL, dar pentru siguranță în interogări scrise manual poate fi citată ("required"). JHipster o generează nescăpată.chk_dep_no_selfșiux_dep_edgenu provin din JDL — recomandate; vezi §8.
2.5 publication_request — fluxul de aprobare pentru publicarea în GPortal
Section titled “2.5 publication_request — fluxul de aprobare pentru publicarea în GPortal”CREATE TABLE publication_request ( id BIGINT NOT NULL PRIMARY KEY, state VARCHAR(32) NOT NULL, requested_by VARCHAR(160), requested_at TIMESTAMPTZ, approved_by VARCHAR(160), approved_at TIMESTAMPTZ, published_at TIMESTAMPTZ, requested_visibility VARCHAR(32), comment VARCHAR(1000), system_id BIGINT NOT NULL, -- OneToMany invers, required -- audit (§6) created_by VARCHAR(50) NOT NULL, created_date TIMESTAMPTZ, last_modified_by VARCHAR(50), last_modified_date TIMESTAMPTZ, CONSTRAINT fk_pubreq_system FOREIGN KEY (system_id) REFERENCES information_system(id), CONSTRAINT chk_pubreq_state CHECK (state IN ( 'DRAFT','PROPOSED','APPROVED','PUBLISHED','REJECTED','UNPUBLISHED' )), CONSTRAINT chk_pubreq_req_vis CHECK (requested_visibility IS NULL OR requested_visibility IN ( 'PUBLIC','INTERNAL','TEST' )));
CREATE INDEX ix_pubreq_system ON publication_request (system_id);CREATE INDEX ix_pubreq_state ON publication_request (state);2.6 api_endpoint — suprafața API expusă de un sistem
Section titled “2.6 api_endpoint — suprafața API expusă de un sistem”CREATE TABLE api_endpoint ( id BIGINT NOT NULL PRIMARY KEY, path VARCHAR(300) NOT NULL, protocol VARCHAR(32) NOT NULL, method VARCHAR(16), description VARCHAR(500), public_api BOOLEAN NOT NULL, system_id BIGINT NOT NULL, -- OneToMany invers, required -- audit (§6) created_by VARCHAR(50) NOT NULL, created_date TIMESTAMPTZ, last_modified_by VARCHAR(50), last_modified_date TIMESTAMPTZ, CONSTRAINT fk_api_system FOREIGN KEY (system_id) REFERENCES information_system(id), CONSTRAINT chk_api_protocol CHECK (protocol IN ('REST','GRAPHQL','SOAP','GRPC','EVENT')));
CREATE INDEX ix_api_system ON api_endpoint (system_id);CREATE INDEX ix_api_public ON api_endpoint (public_api) WHERE public_api = TRUE;2.7 registry_consumer — consumator înregistrat de date de registru
Section titled “2.7 registry_consumer — consumator înregistrat de date de registru”CREATE TABLE registry_consumer ( id BIGINT NOT NULL PRIMARY KEY, name VARCHAR(160) NOT NULL, description VARCHAR(500), filter_expression VARCHAR(200), active BOOLEAN NOT NULL, -- audit (§6) created_by VARCHAR(50) NOT NULL, created_date TIMESTAMPTZ, last_modified_by VARCHAR(50), last_modified_date TIMESTAMPTZ);
CREATE INDEX ix_consumer_active ON registry_consumer (active) WHERE active = TRUE;2.8 registry_audit — jurnal de audit imutabil (append-only)
Section titled “2.8 registry_audit — jurnal de audit imutabil (append-only)”CREATE TABLE registry_audit ( id BIGINT NOT NULL PRIMARY KEY, change_type VARCHAR(32) NOT NULL, actor VARCHAR(160) NOT NULL, summary_ro VARCHAR(500), summary_en VARCHAR(500), ip_address VARCHAR(64), occurred_at TIMESTAMPTZ NOT NULL, system_id BIGINT, -- OneToMany invers, opțional -- audit (§6) — redundant cu actor/occurred_at, păstrat pentru uniformitate created_by VARCHAR(50) NOT NULL, created_date TIMESTAMPTZ, last_modified_by VARCHAR(50), last_modified_date TIMESTAMPTZ, CONSTRAINT fk_audit_system FOREIGN KEY (system_id) REFERENCES information_system(id), CONSTRAINT chk_audit_change_type CHECK (change_type IN ( 'CREATE','EDIT','PUBLISH','UNPUBLISH','ENV_CHANGE', 'DEPENDENCY_CHANGE','STATUS_CHANGE','DELETE' )));
CREATE INDEX ix_audit_system_time ON registry_audit (system_id, occurred_at DESC);CREATE INDEX ix_audit_change_type ON registry_audit (change_type);CREATE INDEX ix_audit_actor ON registry_audit (actor);Append-only impus și la nivel de DB — vezi §5.
system_ideste opțional (relațiaRegistryAudit{system}din JDL nu arerequired): pot exista intrări de audit care nu vizează un sistem anume (ex. acțiuni la nivel de registru).
3. Tabele built-in JHipster (auth)
Section titled “3. Tabele built-in JHipster (auth)”Autentificarea GRegistry este OAuth2 / OIDC (authenticationType oauth2),
delegată către GSSO / Keycloak — nu există login/parolă local, deci nu se
generează tabelele jhi_user / jhi_authority cu password_hash ca la un monolit
JWT. Rămân totuși generate, pentru referință (nu se editează manual):
jhi_user— proiecție locală a utilizatorului OIDC (login, email, activated, lang_key), populată la primul login din claims-urile token-ului.jhi_authority—name PK∈ {ROLE_ADMIN, ROLE_USER} — mapate din rolurile Keycloak.jhi_persistent_audit_event,jhi_persistent_audit_evt_data— audit Spring Boot.databasechangelog,databasechangeloglock— Liquibase.
Rolurile funcționale (ADMIN de registru, editor de sistem, aprobator de
publicare) provin din authorities Spring Security (ROLE_*), mapate din
Keycloak — nu există o entitate de domeniu AppUser separată în acest sistem.
4. Indici suplimentari recomandați
Section titled “4. Indici suplimentari recomandați”-- Calea de citire a consumatorilor (GPortal): "dă-mi sistemele publice publicate".-- Deja acoperit de ix_is_visibility_published; index parțial mai strâns:CREATE INDEX ix_is_portal_feed ON information_system (visibility) WHERE visibility = 'PUBLIC' AND published_to_portal = TRUE;
-- Căutare frecventă după cod (join key folosit de consumatori) — acoperit de-- ux_is_code (unique), dar util pentru prefix-search pe cod:CREATE INDEX ix_is_code_pattern ON information_system (code varchar_pattern_ops);
-- Matricea de dependențe "consumat de X" / "depinde de Y":-- ix_dep_system (system_id, depends_on_id) + ix_dep_depends_on acoperă ambele sensuri.
-- Raport "ce a schimbat actorul X în perioada Y":CREATE INDEX ix_audit_actor_time ON registry_audit (actor, occurred_at DESC);
-- Medii nesănătoase pentru dashboard de monitorizare:CREATE INDEX ix_env_unhealthy ON environment (system_id) WHERE state IN ('DEGRADED','DOWN');
-- Cereri de publicare în lucru (coada de aprobare):CREATE INDEX ix_pubreq_open ON publication_request (state) WHERE state IN ('PROPOSED','APPROVED');
-- Full-text local pe nume/descriere sistem (fără Elasticsearch în GRegistry):CREATE INDEX ix_is_name_fts ON information_system USING gin (to_tsvector('simple', name || ' ' || coalesce(description_ro,'')));5. Trigger append-only pentru registry_audit
Section titled “5. Trigger append-only pentru registry_audit”RegistryAudit este imutabil din perspectiva aplicației (jurnal de audit,
forwardat și către GLog). Se impune și la nivel de DB, nu doar în service layer:
CREATE OR REPLACE FUNCTION registry_audit_no_modify()RETURNS TRIGGER AS $$BEGIN RAISE EXCEPTION 'registry_audit este append-only (% blocat)', TG_OP;END;$$ LANGUAGE plpgsql;
CREATE TRIGGER tr_audit_no_update BEFORE UPDATE ON registry_audit FOR EACH ROW EXECUTE FUNCTION registry_audit_no_modify();
CREATE TRIGGER tr_audit_no_delete BEFORE DELETE ON registry_audit FOR EACH ROW EXECUTE FUNCTION registry_audit_no_modify();Notă: Liquibase rulează cu utilizator privilegiat; pentru migrări care chiar trebuie să atingă istoricul (caz extrem), trigger-ele se dezactivează temporar într-un changelog dedicat și se reactivează imediat după. La nivel de service layer,
RegistryAuditRepositorytrebuie să interzicăsavepe entități existente șidelete(append-only din JDL: „enforce no-update/no-delete in the service layer”).
6. Câmpuri standard de audit
Section titled “6. Câmpuri standard de audit”Toate entitățile moștenesc (de la AbstractAuditingEntity +
SpringSecurityAuditorAware):
| Coloană | Tip | Populare |
|---|---|---|
created_by | VARCHAR(50) NOT NULL | login OIDC al utilizatorului la INSERT |
created_date | TIMESTAMPTZ | Instant.now() la INSERT |
last_modified_by | VARCHAR(50) | login OIDC al utilizatorului la UPDATE |
last_modified_date | TIMESTAMPTZ | Instant.now() la UPDATE |
Pe registry_audit aceste câmpuri coexistă cu actor/occurred_at de domeniu
(vezi nota din §2.8); pe information_system coexistă cu created_at/updated_at
de domeniu (vezi §2.2). Ambele suprapuneri sunt de revizuit în §8.
7. Conversie JSONB — N/A pentru GRegistry
Section titled “7. Conversie JSONB — N/A pentru GRegistry”Spre deosebire de Registrul Interdicțiilor (unde Entitate.atribute,
ActiuneInterzisa.scope, EvenimentInterdictie.payload, Notificare.payload
sunt TextBlob → jsonb), GRegistry nu are niciun câmp TextBlob/JSON.
Toate câmpurile din JDL sunt String (→ VARCHAR), Boolean (→ BOOLEAN),
enum (→ VARCHAR + CHECK) sau Instant (→ TIMESTAMPTZ).
Prin urmare:
- nu există changelog Liquibase de conversie
TEXT → jsonb; - nu există adnotări
@JdbcTypeCode(SqlTypes.JSON)/ hypersistence-utils; - nu există indici GIN pe coloane jsonb (singurul GIN recomandat este cel full-text opțional din §4).
Dacă în viitor system_dependency.note sau api_endpoint.description evoluează
spre metadate structurate (ex. spec OpenAPI inline), se va deschide un ADR și un
changelog de conversie separat — momentan nu e cazul.
8. Checklist de revizuit
Section titled “8. Checklist de revizuit”- Dublă marcă temporală pe
information_system—created_at/updated_at(domeniu, JDL) vs.created_date/last_modified_date(audit). Se păstrează ambele sau se elimină cele de domeniu și se folosesc doar cele de audit? -
registry_auditaudit-pe-audit —created_by/created_datesunt redundante cuactor/occurred_at. Se dezactiveazăAbstractAuditingEntitype această entitate (nu extinde) sau se acceptă redundanța? -
chk_dep_no_selfșiux_dep_edge— nu provin din JDL. Confirmă dacă un sistem poate depinde de sine (probabil nu) și dacă pot exista muchii duplicate consumator→furnizor de acelașikind. - Detecție cicluri în graful de dependențe —
SystemDependencypermite cicluri la nivel de DB. Validarea aciclicității (dacă e cerută) se face în service layer, nu prin constrângere SQL. -
ON DELETEpe FK-uri — la ștergerea unuiinformation_system, ce se întâmplă cuenvironment/api_endpoint/publication_request(CASCADE?) și cusystem_dependency(muchii orfane)?registry_auditnu trebuie să cascadeze (audit nu se șterge — de altfel e append-only). - Soft-delete pe
information_system— pentru un registru autoritar, ștergerea fizică e probabil interzisă; se preferăstatus = RETIRED. Dacă e nevoie de tombstone, adaugădeleted_at TIMESTAMPTZ. - Consistență flux publicare —
information_system.published_to_portaleste denormalizat față depublication_request.state = PUBLISHED. Cine sincronizează flagul (service layer) și cum se previne divergența? - Unicitate
publication_request— se permite o singură cerere activă (state ∈ {DRAFT,PROPOSED,APPROVED}) per sistem? Dacă da → unique parțial. - Lungimi
VARCHAR(32)pentru enum-uri — cea mai lungă valoare esteDEPENDENCY_CHANGE(17) /INFRASTRUCTURE(14); 32 e suficient cu marjă. - Validări impuse doar la aplicație —
provider.code minlength(2),information_system.codepattern^[a-z][a-z0-9-]*$,provider.contact_emailpattern e-mail: sunt Bean Validation, nu constrângeri DB. De decis dacă se adaugăCHECK-uri de oglindire în DB. - Retenție
registry_audit— crește nelimitat + e forwardat la GLog. Politică de arhivare/partitioning peoccurred_at?