@oondemand/create-central-oon 0.3.40 → 0.3.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# Manifesto declarativo de domínio — `central.domain.json`
|
|
2
2
|
|
|
3
|
-
O arquivo `central.domain.json`, localizado na raiz do backend da Central, declara models e
|
|
3
|
+
O arquivo `central.domain.json`, localizado na raiz do backend da Central, declara models, campos, fórmulas e validações sem exigir um arquivo JavaScript por model.
|
|
4
4
|
|
|
5
5
|
O OonCore carrega o manifesto durante o bootstrap, antes de carregar `src/models`, `src/validations`, `src/triggers` e os demais diretórios de extensão.
|
|
6
6
|
|
|
7
|
-
> A Central declara o domínio. O OonCore constrói schema Mongoose, metadata, CRUD e
|
|
7
|
+
> A Central declara o domínio e as regras. O OonCore constrói schema Mongoose, metadata, CRUD, cálculos protegidos e validações.
|
|
8
8
|
|
|
9
9
|
## Escopo da versão 1
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
O contrato cobre:
|
|
12
12
|
|
|
13
13
|
- identidade do manifesto;
|
|
14
14
|
- models e seus caminhos de API;
|
|
@@ -16,21 +16,24 @@ A primeira versão do contrato cobre:
|
|
|
16
16
|
- campos primitivos, enumerações, referências e moedas;
|
|
17
17
|
- obrigatoriedade, valor padrão, busca, unicidade e índice simples;
|
|
18
18
|
- limites numéricos e de tamanho de texto;
|
|
19
|
-
- campos somente leitura
|
|
19
|
+
- campos somente leitura protegidos nas mutações HTTP;
|
|
20
|
+
- campos calculados no servidor;
|
|
21
|
+
- dependências entre campos calculados, ordenadas automaticamente;
|
|
22
|
+
- precisão e tratamento de valores ausentes em fórmulas;
|
|
23
|
+
- validações declarativas entre campos, com condição opcional;
|
|
20
24
|
- validação estrutural com todos os problemas retornados em uma única exceção.
|
|
21
25
|
|
|
22
|
-
Ainda não fazem parte
|
|
26
|
+
Ainda não fazem parte desta versão:
|
|
23
27
|
|
|
24
|
-
- fórmulas e campos calculados;
|
|
25
28
|
- índices compostos;
|
|
26
|
-
-
|
|
27
|
-
- triggers e transições;
|
|
29
|
+
- triggers e transições declarativas;
|
|
28
30
|
- migrações automáticas de dados;
|
|
29
|
-
- mappings de integração
|
|
31
|
+
- mappings de integração;
|
|
32
|
+
- funções JavaScript embutidas no JSON.
|
|
30
33
|
|
|
31
|
-
|
|
34
|
+
As expressões são interpretadas por um avaliador fechado. O Core não usa `eval`, `Function` ou execução de código vindo do manifesto.
|
|
32
35
|
|
|
33
|
-
## Exemplo
|
|
36
|
+
## Exemplo financeiro
|
|
34
37
|
|
|
35
38
|
```json
|
|
36
39
|
{
|
|
@@ -39,49 +42,103 @@ Esses recursos serão adicionados em contratos próprios ou em versões posterio
|
|
|
39
42
|
"schemaVersion": 1,
|
|
40
43
|
"models": [
|
|
41
44
|
{
|
|
42
|
-
"name": "
|
|
43
|
-
"singular": "
|
|
44
|
-
"basePath": "/
|
|
45
|
+
"name": "ProjetoItem",
|
|
46
|
+
"singular": "item",
|
|
47
|
+
"basePath": "/itens",
|
|
45
48
|
"crud": {
|
|
46
49
|
"enabled": true
|
|
47
50
|
},
|
|
48
51
|
"fields": {
|
|
49
|
-
"
|
|
50
|
-
"kind": "
|
|
51
|
-
"
|
|
52
|
-
"required": true,
|
|
53
|
-
"searchable": true,
|
|
54
|
-
"minLength": 2
|
|
52
|
+
"quantidade": {
|
|
53
|
+
"kind": "number",
|
|
54
|
+
"required": true
|
|
55
55
|
},
|
|
56
|
-
"
|
|
57
|
-
"kind": "
|
|
58
|
-
"
|
|
59
|
-
"values": ["PF", "PJ", "Est"],
|
|
60
|
-
"default": "PJ"
|
|
56
|
+
"diarias": {
|
|
57
|
+
"kind": "number",
|
|
58
|
+
"required": true
|
|
61
59
|
},
|
|
62
|
-
"
|
|
63
|
-
"kind": "
|
|
64
|
-
"
|
|
65
|
-
"unique": true,
|
|
66
|
-
"index": true
|
|
60
|
+
"valorUnitario": {
|
|
61
|
+
"kind": "currency",
|
|
62
|
+
"required": true
|
|
67
63
|
},
|
|
68
|
-
"
|
|
69
|
-
"kind": "
|
|
70
|
-
"
|
|
71
|
-
"ref": "Responsavel"
|
|
64
|
+
"percentualFee": {
|
|
65
|
+
"kind": "number",
|
|
66
|
+
"default": 0
|
|
72
67
|
},
|
|
73
|
-
"
|
|
74
|
-
"kind": "
|
|
75
|
-
"
|
|
76
|
-
"default": true
|
|
68
|
+
"valorContratado": {
|
|
69
|
+
"kind": "currency",
|
|
70
|
+
"default": 0
|
|
77
71
|
},
|
|
78
|
-
"
|
|
72
|
+
"valorPago": {
|
|
79
73
|
"kind": "currency",
|
|
80
|
-
"
|
|
81
|
-
|
|
74
|
+
"default": 0
|
|
75
|
+
},
|
|
76
|
+
"statusIntegracao": {
|
|
77
|
+
"kind": "string",
|
|
82
78
|
"readonly": true
|
|
79
|
+
},
|
|
80
|
+
"subtotal": {
|
|
81
|
+
"kind": "currency",
|
|
82
|
+
"computed": {
|
|
83
|
+
"precision": 2,
|
|
84
|
+
"expression": {
|
|
85
|
+
"op": "multiply",
|
|
86
|
+
"args": [
|
|
87
|
+
{ "field": "quantidade" },
|
|
88
|
+
{ "field": "diarias" },
|
|
89
|
+
{ "field": "valorUnitario" }
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
"valorFee": {
|
|
95
|
+
"kind": "currency",
|
|
96
|
+
"computed": {
|
|
97
|
+
"precision": 2,
|
|
98
|
+
"expression": {
|
|
99
|
+
"op": "divide",
|
|
100
|
+
"args": [
|
|
101
|
+
{
|
|
102
|
+
"op": "multiply",
|
|
103
|
+
"args": [
|
|
104
|
+
{ "field": "subtotal" },
|
|
105
|
+
{ "field": "percentualFee" }
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
{ "value": 100 }
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
"total": {
|
|
114
|
+
"kind": "currency",
|
|
115
|
+
"computed": {
|
|
116
|
+
"precision": 2,
|
|
117
|
+
"expression": {
|
|
118
|
+
"op": "add",
|
|
119
|
+
"args": [
|
|
120
|
+
{ "field": "subtotal" },
|
|
121
|
+
{ "field": "valorFee" }
|
|
122
|
+
]
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
"validations": [
|
|
128
|
+
{
|
|
129
|
+
"name": "pagamento-limitado-ao-contratado",
|
|
130
|
+
"code": "PAGAMENTO_ACIMA_CONTRATADO",
|
|
131
|
+
"field": "valorPago",
|
|
132
|
+
"message": "O valor pago não pode superar o valor contratado.",
|
|
133
|
+
"assert": {
|
|
134
|
+
"op": "lte",
|
|
135
|
+
"args": [
|
|
136
|
+
{ "field": "valorPago" },
|
|
137
|
+
{ "field": "valorContratado" }
|
|
138
|
+
]
|
|
139
|
+
}
|
|
83
140
|
}
|
|
84
|
-
|
|
141
|
+
]
|
|
85
142
|
}
|
|
86
143
|
]
|
|
87
144
|
}
|
|
@@ -106,6 +163,7 @@ Esses recursos serão adicionados em contratos próprios ou em versões posterio
|
|
|
106
163
|
| `crud` | não | Mesmo contrato aceito por `defineModel`. |
|
|
107
164
|
| `options` | não | Opções JSON compatíveis com o schema Mongoose. |
|
|
108
165
|
| `fields` | sim | Objeto com pelo menos um campo. |
|
|
166
|
+
| `validations` | não | Lista de regras declarativas executadas depois dos cálculos. |
|
|
109
167
|
|
|
110
168
|
Não declare a mesma model no manifesto e em `src/models`. O registry interrompe o bootstrap para impedir duas fontes de verdade.
|
|
111
169
|
|
|
@@ -127,10 +185,11 @@ Não declare a mesma model no manifesto e em `src/models`. O registry interrompe
|
|
|
127
185
|
- `description`: explicação funcional;
|
|
128
186
|
- `required`: campo obrigatório;
|
|
129
187
|
- `default`: valor padrão JSON;
|
|
130
|
-
- `readonly`:
|
|
188
|
+
- `readonly`: campo controlado pelo servidor;
|
|
131
189
|
- `searchable`: inclui texto na busca derivada do Core;
|
|
132
190
|
- `unique`: índice único simples;
|
|
133
|
-
- `index`: índice simples
|
|
191
|
+
- `index`: índice simples;
|
|
192
|
+
- `computed`: fórmula declarativa para campos numéricos ou monetários.
|
|
134
193
|
|
|
135
194
|
### Opções por tipo
|
|
136
195
|
|
|
@@ -140,9 +199,172 @@ Não declare a mesma model no manifesto e em `src/models`. O registry interrompe
|
|
|
140
199
|
- `enum`: `values` com textos únicos e não vazios;
|
|
141
200
|
- `currencyConverted`: `base` com código ISO de três letras.
|
|
142
201
|
|
|
143
|
-
##
|
|
202
|
+
## Campos calculados
|
|
203
|
+
|
|
204
|
+
`computed` é permitido em `number`, `currency` e `currencyConverted`.
|
|
205
|
+
|
|
206
|
+
```json
|
|
207
|
+
{
|
|
208
|
+
"kind": "currency",
|
|
209
|
+
"computed": {
|
|
210
|
+
"expression": {
|
|
211
|
+
"op": "multiply",
|
|
212
|
+
"args": [
|
|
213
|
+
{ "field": "quantidade" },
|
|
214
|
+
{ "field": "valorUnitario" }
|
|
215
|
+
]
|
|
216
|
+
},
|
|
217
|
+
"precision": 2,
|
|
218
|
+
"nullAsZero": true
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
| Propriedade | Padrão | Descrição |
|
|
224
|
+
|---|---:|---|
|
|
225
|
+
| `expression` | — | Expressão obrigatória. |
|
|
226
|
+
| `precision` | `2` | Casas decimais, entre 0 e 8. |
|
|
227
|
+
| `nullAsZero` | `true` | Trata campos ausentes ou vazios como zero nas operações numéricas. |
|
|
228
|
+
|
|
229
|
+
Campos calculados:
|
|
230
|
+
|
|
231
|
+
- são automaticamente `readonly`;
|
|
232
|
+
- recebem `immutable` no schema Mongoose;
|
|
233
|
+
- são recalculados no backend em criação, edição, patch e importação;
|
|
234
|
+
- são calculados em ordem de dependência;
|
|
235
|
+
- não podem formar ciclos;
|
|
236
|
+
- aparecem na metadata com `readonly: true` e a declaração `computed`.
|
|
237
|
+
|
|
238
|
+
## Expressões
|
|
239
|
+
|
|
240
|
+
Uma expressão declara exatamente um destes nós:
|
|
241
|
+
|
|
242
|
+
```json
|
|
243
|
+
{ "value": 100 }
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
```json
|
|
247
|
+
{ "field": "valorUnitario" }
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
```json
|
|
251
|
+
{
|
|
252
|
+
"op": "multiply",
|
|
253
|
+
"args": [
|
|
254
|
+
{ "field": "quantidade" },
|
|
255
|
+
{ "field": "valorUnitario" }
|
|
256
|
+
]
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Operadores aritméticos
|
|
261
|
+
|
|
262
|
+
- `add`
|
|
263
|
+
- `subtract`
|
|
264
|
+
- `multiply`
|
|
265
|
+
- `divide`
|
|
266
|
+
- `min`
|
|
267
|
+
- `max`
|
|
268
|
+
- `abs`
|
|
269
|
+
- `negate`
|
|
270
|
+
- `coalesce`
|
|
271
|
+
|
|
272
|
+
### Operadores de comparação
|
|
273
|
+
|
|
274
|
+
- `eq`
|
|
275
|
+
- `neq`
|
|
276
|
+
- `gt`
|
|
277
|
+
- `gte`
|
|
278
|
+
- `lt`
|
|
279
|
+
- `lte`
|
|
280
|
+
|
|
281
|
+
### Operadores lógicos e de presença
|
|
282
|
+
|
|
283
|
+
- `and`
|
|
284
|
+
- `or`
|
|
285
|
+
- `not`
|
|
286
|
+
- `present`
|
|
287
|
+
- `in`
|
|
288
|
+
|
|
289
|
+
Divisão por zero e valores não numéricos em fórmulas geram `DomainRuleError` com status 422.
|
|
290
|
+
|
|
291
|
+
## Validações entre campos
|
|
292
|
+
|
|
293
|
+
As validações rodam depois que o Core consolidou o registro e recalculou todos os campos dependentes.
|
|
294
|
+
|
|
295
|
+
```json
|
|
296
|
+
{
|
|
297
|
+
"name": "valor-pago-valido",
|
|
298
|
+
"code": "PAGAMENTO_ACIMA_CONTRATADO",
|
|
299
|
+
"field": "valorPago",
|
|
300
|
+
"message": "O valor pago não pode superar o valor contratado.",
|
|
301
|
+
"when": {
|
|
302
|
+
"op": "present",
|
|
303
|
+
"args": [{ "field": "valorPago" }]
|
|
304
|
+
},
|
|
305
|
+
"assert": {
|
|
306
|
+
"op": "lte",
|
|
307
|
+
"args": [
|
|
308
|
+
{ "field": "valorPago" },
|
|
309
|
+
{ "field": "valorContratado" }
|
|
310
|
+
]
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
| Propriedade | Obrigatória | Descrição |
|
|
316
|
+
|---|---:|---|
|
|
317
|
+
| `name` | sim | Identificador único da validação dentro da model. |
|
|
318
|
+
| `message` | sim | Mensagem operacional apresentada ao usuário. |
|
|
319
|
+
| `assert` | sim | Expressão que deve resultar em verdadeiro. |
|
|
320
|
+
| `when` | não | Condição para executar a regra. |
|
|
321
|
+
| `field` | não | Campo associado ao erro. |
|
|
322
|
+
| `code` | não | Código em maiúsculas para tratamento programático. |
|
|
323
|
+
|
|
324
|
+
Falhas geram `DomainRuleError` com `statusCode: 422`, `code`, `field`, `rule` e detalhes compatíveis com o tratamento de erros do Core.
|
|
325
|
+
|
|
326
|
+
## Proteção de campos somente leitura
|
|
327
|
+
|
|
328
|
+
A proteção não depende apenas do frontend.
|
|
329
|
+
|
|
330
|
+
- valor readonly enviado na criação é rejeitado;
|
|
331
|
+
- alteração de valor readonly é rejeitada;
|
|
332
|
+
- em edição, o mesmo valor pode voltar no payload e é removido antes da persistência;
|
|
333
|
+
- campos calculados podem voltar no payload somente quando coincidem com o resultado calculado pelo servidor;
|
|
334
|
+
- tentativa de adulterar um campo calculado é rejeitada;
|
|
335
|
+
- services recebem somente campos permitidos e os resultados recalculados.
|
|
336
|
+
|
|
337
|
+
Isso permite formulários que enviam o registro completo sem abrir espaço para alterar totais, status técnicos ou identificadores controlados pelo sistema.
|
|
338
|
+
|
|
339
|
+
## Atualizações parciais
|
|
340
|
+
|
|
341
|
+
Em `PUT` ou `PATCH`, o Core:
|
|
342
|
+
|
|
343
|
+
1. carrega o registro atual quando existem regras declarativas ou `defineValidation`;
|
|
344
|
+
2. remove ou bloqueia campos readonly;
|
|
345
|
+
3. consolida os campos atuais com as alterações recebidas;
|
|
346
|
+
4. recalcula campos dependentes em ordem;
|
|
347
|
+
5. executa validações declarativas;
|
|
348
|
+
6. executa a validação JavaScript registrada, quando existir;
|
|
349
|
+
7. persiste somente as alterações permitidas e os valores calculados.
|
|
350
|
+
|
|
351
|
+
A validação JavaScript recebe:
|
|
352
|
+
|
|
353
|
+
```js
|
|
354
|
+
{
|
|
355
|
+
op,
|
|
356
|
+
method,
|
|
357
|
+
id,
|
|
358
|
+
current,
|
|
359
|
+
requestedChanges,
|
|
360
|
+
changes,
|
|
361
|
+
consolidated
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
## Erros de validação do manifesto
|
|
144
366
|
|
|
145
|
-
Um manifesto inválido lança `DomainManifestError`:
|
|
367
|
+
Um manifesto estruturalmente inválido lança `DomainManifestError`:
|
|
146
368
|
|
|
147
369
|
```js
|
|
148
370
|
{
|
|
@@ -151,8 +373,8 @@ Um manifesto inválido lança `DomainManifestError`:
|
|
|
151
373
|
statusCode: 422,
|
|
152
374
|
issues: [
|
|
153
375
|
{
|
|
154
|
-
path: "models[0].fields.
|
|
155
|
-
message: "
|
|
376
|
+
path: "models[0].fields.total.computed.expression",
|
|
377
|
+
message: "dependência circular entre campos calculados: total -> fee -> total."
|
|
156
378
|
}
|
|
157
379
|
]
|
|
158
380
|
}
|
|
@@ -168,17 +390,22 @@ const {
|
|
|
168
390
|
domainManifestToDefinitions,
|
|
169
391
|
registerDomainManifest,
|
|
170
392
|
loadDomainManifest,
|
|
393
|
+
evaluateDomainExpression,
|
|
394
|
+
applyDomainMutation,
|
|
395
|
+
DomainManifestError,
|
|
396
|
+
DomainRuleError,
|
|
397
|
+
DOMAIN_EXPRESSION_OPERATORS
|
|
171
398
|
} = require("@oondemand/oon-core-back");
|
|
172
399
|
```
|
|
173
400
|
|
|
174
|
-
Na operação normal não é necessário chamar essas funções: `oonCore-back start` descobre automaticamente `central.domain.json
|
|
401
|
+
Na operação normal não é necessário chamar essas funções: `oonCore-back start` descobre automaticamente `central.domain.json` e o CRUD aplica as regras.
|
|
175
402
|
|
|
176
403
|
## Compatibilidade durante a migração
|
|
177
404
|
|
|
178
|
-
Os diretórios JavaScript continuam disponíveis para
|
|
405
|
+
Os diretórios JavaScript continuam disponíveis para regras ainda não declarativas. A ordem é:
|
|
179
406
|
|
|
180
407
|
1. `central.config.js`;
|
|
181
408
|
2. `central.domain.json`;
|
|
182
409
|
3. diretórios em `src/`.
|
|
183
410
|
|
|
184
|
-
Isso permite migrar model por model
|
|
411
|
+
Isso permite migrar model por model. `defineValidation` continua disponível e roda depois das fórmulas e validações declarativas.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# OonCore 0.3.41 — Manifesto declarativo de domínio
|
|
2
|
+
|
|
3
|
+
Versão destinada à homologação do primeiro contrato declarativo completo de domínio no backend.
|
|
4
|
+
|
|
5
|
+
## Recursos incluídos
|
|
6
|
+
|
|
7
|
+
- carregamento automático de `central.domain.json`;
|
|
8
|
+
- declaração versionada de models e campos;
|
|
9
|
+
- validação estrutural agregada;
|
|
10
|
+
- fórmulas declarativas sem execução de JavaScript arbitrário;
|
|
11
|
+
- campos calculados no servidor;
|
|
12
|
+
- ordenação de dependências e detecção de ciclos;
|
|
13
|
+
- proteção de campos `readonly` em criação, atualização e importação;
|
|
14
|
+
- validações declarativas entre campos;
|
|
15
|
+
- atualização parcial com consolidação e recálculo;
|
|
16
|
+
- compatibilidade com `defineValidation` durante a migração gradual.
|
|
17
|
+
|
|
18
|
+
## Objetivo da homologação
|
|
19
|
+
|
|
20
|
+
Validar o contrato em uma Central de teste antes da conversão dos models e regras da SS-Eventos.
|
|
21
|
+
|
|
22
|
+
A homologação deve confirmar:
|
|
23
|
+
|
|
24
|
+
1. criação e carregamento do manifesto;
|
|
25
|
+
2. geração de metadata e CRUD;
|
|
26
|
+
3. recálculo de campos em `POST`, `PUT` e `PATCH`;
|
|
27
|
+
4. rejeição de adulteração em campos controlados pelo servidor;
|
|
28
|
+
5. mensagens de validação associadas ao campo correto;
|
|
29
|
+
6. compatibilidade com models e validações JavaScript ainda não migrados.
|
|
30
|
+
|
|
31
|
+
## Fora do escopo
|
|
32
|
+
|
|
33
|
+
- fórmulas reativas no frontend;
|
|
34
|
+
- índices compostos;
|
|
35
|
+
- triggers e transições declarativas;
|
|
36
|
+
- engine genérica de integrações;
|
|
37
|
+
- migração da SS-Eventos.
|
package/package.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"sync:metadata": "oonCore-front sync:metadata"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@oondemand/oon-core-front": "^0.3.
|
|
13
|
+
"@oondemand/oon-core-front": "^0.3.41",
|
|
14
14
|
"@chakra-ui/react": "^3.13.0",
|
|
15
15
|
"@emotion/react": "^11.14.0",
|
|
16
16
|
"@tanstack/react-query": "^5.65.0",
|