Guia de Desenvolvimento: Criando um Novo Módulo
Para manter a consistência e o isolamento da arquitetura em nosso ecossistema de saúde, a criação de um novo domínio (ex: prescriptions, exams, telemetry) deve seguir um padrão rigoroso. Este guia detalha o processo de "scaffolding" e as regras de implementação passo a passo.
Passo 1: Preparação e Scaffolding
Cada novo módulo deve ser criado dentro de packages/modules/[nome-do-modulo].
Criação do Diretório: No terminal (raiz do projeto):
bashmkdir -p packages/modules/my-new-module/{api,store,machines,ui,hooks,utils} touch packages/modules/my-new-module/index.ts touch packages/modules/my-new-module/README.mdEstrutura Interna Esperada:
textpackages/modules/[nome-do-modulo]/ ├── api/ # Chamadas HTTP/Axios exclusivas do módulo ├── store/ # Lógica Redux (Slices, Selectors, Sagas) │ ├── slice.ts │ ├── selectors.ts │ ├── sagas.ts │ └── types.ts ├── machines/ # Máquinas de estado (XState) ├── ui/ # Componentes React │ ├── components/ # Componentes internos (não exportados) │ ├── Page.tsx # Entry point da página (se aplicável) │ └── index.ts # Exportações públicas da UI ├── index.ts # Public API do Pacote (Gatekeeper) └── README.md # Documentação técnica do módulo
Passo 2: Definindo o Contrato (Contracts)
Antes de codificar a lógica, defina os tipos de dados em packages/contracts. Isso garante o "Single Source of Truth" para tipagem.
- Crie o arquivo
packages/contracts/src/my-new-domain.ts. - Adicione as interfaces de negócio:typescript
export interface MyData { id: string; status: 'PENDING' | 'COMPLETED'; createdAt: string; } - Exporte no
packages/contracts/src/index.ts.
Passo 3: Implementando a Camada de Dados (Store)
O módulo deve gerenciar seu próprio estado de forma isolada.
Slice (
store/slice.ts):typescriptimport { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { MyData } from 'contracts/my-new-domain'; interface DomainState { items: MyData[]; loading: boolean; } const initialState: DomainState = { items: [], loading: false }; const domainSlice = createSlice({ name: 'myNewDomain', initialState, reducers: { fetchDataRequest: (state) => { state.loading = true; }, fetchDataSuccess: (state, action: PayloadAction<MyData[]>) => { state.items = action.payload; state.loading = false; } } }); export const { actions: domainActions, reducer: domainReducer } = domainSlice;Sagas (
store/sagas.ts): Implemente a lógica de efeitos colaterais (chamadas de API).
Passo 4: Public API e Encapsulamento
O arquivo index.ts na raiz do módulo é a única porta de entrada permitida.
- Edite
packages/modules/my-new-module/index.ts:{typescript// 1. Exporta Componentes de UI export { MyDomainPage } from './ui/Page'; // 2. Exporta Lógica de Estado para Injeção Dinâmica export { domainReducer } from './store/slice'; export { domainSaga } from './store/sagas'; // 3. Exporta Actions e Selectors necessários para a App export { domainActions } from './store/slice'; export { selectMyData } from './store/selectors';
Passo 5: Registro e Consumo na Aplicação (App)
As aplicações orquestram os domínios. Siga estes passos para injetar o domínio:
Importação:
tsximport { useInjectReducer, useInjectSaga } from 'core/store-injector'; import { moduleReducer, moduleSaga, MyModulePage } from 'modules/my-new-module';Injeção no Componente de Rota:
tsxconst MyRoute = () => { // Passo Crítico: Injetar os recursos antes de renderizar a UI useInjectReducer({ key: 'myNewModule', reducer: moduleReducer }); useInjectSaga({ key: 'myNewModule', saga: moduleSaga }); return <MyModulePage />; };
Regras de Ouro (Checklist)
- [ ] Meu módulo importa algo de outro módulo? (Deve ser NÃO).
- [ ] Meus tipos estão em
packages/contracts? (Deve ser SIM). - [ ] O
index.tsexporta apenas o estritamente necessário? (Deve ser SIM). - [ ] Usei o Design System de
packages/shared/ui? (Deve ser SIM).