Skip to content

Instantly share code, notes, and snippets.

@luizsn00
Last active October 25, 2023 00:00
Show Gist options
  • Save luizsn00/72474683586200b6096fd387477b69e3 to your computer and use it in GitHub Desktop.
Save luizsn00/72474683586200b6096fd387477b69e3 to your computer and use it in GitHub Desktop.
MVVM - REACT SAMPLE

MVVM | React Sample

Exemplo de implementação de uma (View/Screen)

Camada Tipo Dependência
SampleProvider Componente -
SampleModel Interface -
SampleViewModel Classe -
SampleController Componente ViewModel
SampleView Componente -

SampleProvider

Responsável por criar a instância da ViewModel, injetá-la no Controller e retornar esse mesmo Controller.

🚀 Insights

  1. Pode ser eliminado ao automatizar a injeção da dependência dentro do Controller
  2. É o ponto inicial da estrutura

SampleModel

É uma Interface responsável por definir os contratos de entrada e saída de dados da View/Screen.

🚀 Insights

  1. Pode extender uma interface base, tipo CRUD para definir padrões de atualização e busca de dados
  2. É o único contrato entre a camada de Domínio (Regra de negócios) e a ViewModel

SampleViewModel

Sua responsabilidade é implementar o Model, pode realizar transformações de dados que o Domínio (Regra de negócios) espera, tratar respostas para a camada de View e capturar e monitorar as respostas e as requisições feitas para a camada de Domínio.

SampleController

Responsável por controlar estados relativos a View/Screen (exceto estados específicos de regra de UI, por exemplo: estados de controle de animações). Recebe a referência a ViewModel e sempre à utiliza para realizar operações.

É aqui que fazemos o uso dos hooks.

Os hooks podem receber a referência de ViewModel através do Controller e nunca ficam acoplados com a camada de domínio.

SampleView

Última camada da estrutura, apenas recebe parâmetros, não consome hooks (exceto casos que o hook manipule apenas visualização dessa própria View). Sua única forma de comunicação é via funções de callback. Em resumo é apenas o Layout, importa e utiliza componentes mais simples para montar o resultado final.


Prós

✅ Toda View é uma tela, garante consistência

✅ Toda tela terá um contrato com a camada de Domínio

✅ A manutenção é facilitada, nenhuma regra se mistura

✅ Fácil migração para outros frameworks e plataformas, a camada de ViewModel não conhece a View, e vice-versa.

✅ Inversão de controle e injeção das dependências

✅ Fácil de testar

✅ Fácil de documentar

Contras

❌ Complexidade inicial, sair do completo zero até esse padrão pode ser um pouco difícil

❌ Maior quantidade de arquivos para serem criados. Pode ser resolvido com PlopJS

❌ Pode ser necessário adicionar/criar plugins ESLINT para impedir que má-práticas, como nomeclaturas incorretas e/ou utilização de um não-padrão voltem a acontecer

export { SampleProvider as Sample } from "./SampleProvider";
import { z } from "zod";
import { SampleModel } from "./SampleModel";
import { updateSampleSchema } from "./schemas";
type UpdateUserData = z.infer<typeof updateSampleSchema>;
type SampleViewProps = {
btnText: string;
btnCb: (params: UpdateUserData) => void;
};
type SampleControllerProps = {
viewModel: SampleModel;
};
export type {
SampleViewProps,
SampleControllerProps,
UpdateUserData,
};
import { UpdateUserData, SampleControllerProps } from "./Sample.types";
function SampleController({ viewModel }: SampleControllerProps) {
const handleClickToUpdate = ((data): UpdateUserData) => {
viewModel.update(data)
}
return <SampleView btnText={'click me!'} btnCb={handleClickToUpdate} />
}
export { SampleController };
import { UpdateUserData } from "./Sample.types";
interface SampleModel {
update: (data: UpdateUserData) => void;
}
export type { SampleModel };
import { SampleViewModel } from "./SampleViewModel";
import { SampleController } from "./SampleController";
const viewModel = new SampleViewModel();
function SampleProvider() {
return <SampleController viewModel={viewModel} />;
}
export { SampleProvider };
import { Button } from "../global/components/primitives/Button";
import { SampleViewProps } from "./SampleView.types";
function SampleView({ btnText, btnCb }: SampleViewProps) {
const handleClick = () => {
btnCb({
name: "Luiz",
email: "[email protected]",
address: "Av Brasil",
permissions: [],
phone: "+55 99 999999999",
rule: "CEO",
});
};
return <Button.Default onClick={handleClick}>{btnText}</Button.Default>;
}
export { SampleView };
import { UpdateUserData } from "./Sample.types";
import { SampleModel } from "./SampleModel";
class SampleViewModel implements SampleModel {
update(data: UpdateUserData) {
// call your useCase here
console.log("> update colab", data);
}
}
export { SampleViewModel };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment