Zum Hauptinhalt springen
••
Sichere ProgrammierungCryptotremor: finding out when your encryption actually stops working(03.10.2026 um 08:10 Uhr)
•
Sichere ProgrammierungAI Viva Partner: A Private, Offline Exam Study Assistant for My Friend(03.10.2026 um 08:11 Uhr)
••
Windows Tipps & SecurityMintline: prove which Solana token came first(03.10.2026 um 08:15 Uhr)
•••
Sichere ProgrammierungI've never run an AI model on my own machine. This month I will.(03.10.2026 um 08:17 Uhr)
•
Sichere ProgrammierungI removed my Chinese web font. Every page lost 1 MB(03.10.2026 um 08:18 Uhr)
•••
Sichere ProgrammierungCryptotremor: finding out when your encryption actually stops working(03.10.2026 um 08:10 Uhr)
•
Sichere ProgrammierungAI Viva Partner: A Private, Offline Exam Study Assistant for My Friend(03.10.2026 um 08:11 Uhr)
••
Windows Tipps & SecurityMintline: prove which Solana token came first(03.10.2026 um 08:15 Uhr)
•••
Sichere ProgrammierungI've never run an AI model on my own machine. This month I will.(03.10.2026 um 08:17 Uhr)
•
Sichere ProgrammierungI removed my Chinese web font. Every page lost 1 MB(03.10.2026 um 08:18 Uhr)
•
Intelligence View
⚡ tsecurity.de Intelligence

Uma Abordagem Funcional para Domain-Driven Design

Há algumas semanas, eu defini a meta de aprender TypeScript e desenvolver um produto do zero utilizando tudo o que a linguagem de programação tem a oferecer. Se…

Beitrag
0
Seite
0
↗ Quelle (dev.to)
Social ReaktionenReagiere als Erste:r — dein Feedback zählt!

Há algumas semanas, eu defini a meta de aprender TypeScript e desenvolver um produto do zero utilizando tudo o que a linguagem de programação tem a oferecer. Sendo alguém com experiência trabalhando com orientação a objetos, migrar para um ambiente multiparadigma me pareceu bastante interessante. Um dos desafios foi trazer os conceitos aplicados em aplicações C# e .NET para o mundo do Node.js, e um desses conceitos foi o Domain-Driven Design.



Explicando de forma breve, Domain-Driven Design, ou DDD, é uma abordagem de desenvolvimento de software que foca na complexidade do domínio de negócio, trazendo-o para o centro do desenvolvimento e buscando aproximar os especialistas desse domínio do processo de criação dos sistemas. Ele te fornece algumas ferramentas para lidar melhor com essa complexidade.



Dentro dessa abordagem, você encontra o Design Estratégico: ele lida com a visão macro, com a estrutura do domínio, com os limites do negócio e com o vocabulário dos especialistas. É aqui que se trabalha com Mapas de Contexto, Contextos Delimitados, subdomínios, Domínio Básico, Domínio de Suporte, Domínio Genérico e Linguagem Ubíqua. Já o Design Tático entra na parte “concreta” da modelagem dentro de cada Bounded Context. É aqui que lidamos com Entidades, Objetos de Valor, Eventos de Domínio, entre outros.



Os padrões estratégicos se adaptam bem a qualquer linguagem, por isso, o foco do texto será exclusivamente no Design Tático.



O TypeScript fornece suporte a classes — não tão ricas quanto no C#, mas bastante flexíveis de trabalhar. E, enquanto desenvolvia um projeto eu pensei: utilizando em uma linguagem multiparadigma, é possível trabalhar com DDD incorporando elementos da programação funcional? E a resposta é sim!



Um dos meu livros de referência Implementando Domain-Driven Design, o foco do DDD Tático fica em linguagens orientadas a objetos, o que me fez buscar outras fontes. Dentre vários artigos lidos (referências ao final da página), cheguei a um projeto simples, mas bastante didático, adaptando conceitos e tentando criar algo a partir disso para quem, assim como eu, também tem essa dúvida.






Entidades e Regras de Negócio



No DDD orientado a objetos, as entidades são normalmente classes possuindo estado e comportamento. Regras e invariantes tendem a ficar dentro das classes de domínio. Esse estilo usa construtores, métodos de instância e encapsulamento por visibilidade (private). Em uma abordagem funcional, a ideia é separar os dados (tipos imutáveis) de comportamentos (funções puras). Em vez de métodos que mutam, temos funções que recebem dados e retornam novos dados ou outcomes.




type BaseEntity<T> = T & { Id: number, IsActive: boolean };

export type User = BaseEntity<{
Username: string,
Email: string,
Password: string,
PhoneNumber: string,
Address: Adderss
}>;







Quando falamos de funções puras, um conceito importante são as Invariants. Em matemática, um invariante é uma propriedade de um objeto matemático que permanece inalterada após a aplicação de operações ou transformações de determinado tipo sobre esse objeto. No nosso caso, esse conceito se aplica a funções pequenas (predicates) que respondem perguntas atômicas do domínio, ex.: isPasswordStrong, isAddressInCoverage




export const IsValidEmail = (email: string) : boolean =>  emailPattern.test(email);
export const IsValidPassWord = (password: string) : boolean => passwordPattern.test(password);
export const IsUserActive = (isActive: boolean) : boolean => isActive === true;






Outro conceito importante são os Derivers. Eles formam o núcleo funcional do sistema, concentrando as regras de negócio puras. São compostos por invariantes e têm como resultado um Outcome (uma discriminated union) ou um novo delta/novo. Eles não realizam I/O, não criam entidades finais e não fazem persistência.



O Deriver faz apenas duas coisas (dependendo do caso):




  • Decidir: responde à pergunta “pode/como essa operação acontecer?” retornando um Outcome (ex.: INVALID_EMAIL, WEAK_PASSWORD, SUCCEEDED).


  • Derivar estado: calcular um novo estado/delta a partir dos dados (ex.: calcular passwordHash, normalizar email, calcular novo carrinho) — Transform Deriver.





const DeriveUser = async (dto: UserDtoType) : CreateUserOutcome => {
if(IsValidEmail(dto.Email))
return { outcome: "INVALID_EMAIL" }
if(IsValidPassWord(dto.Password))
return { outcome: 'INVALID_PASSWORD' }
return {
outcome: 'SUCCEEDED',
payload: {
normalizedEmail: dto.Email,
hashedPassword: await HashPassword(dto.Password),
username: dto.Username
}
}
}






Quando falo em “derivar um novo estado” não quero dizer “aplicar esse novo estado ao banco/sistema”. O Deriver retorna um valor (novo objeto em memória) que representa como o estado deveria ficar. Persistir esse novo estado é responsabilidade do Handler.



Outcome: union discriminado que descreve todas as possibilidades de resultado. É o contrato pelo qual o Deriver comunica decisões ao Handler. Exemplo:




export type UserOutcome =
| { outcome: "INVALID_EMAIL" }
| { outcome: "INVALID_PASSWORD" }
| { outcome: "INVALID_PHONE" }
| { outcome: "INVALID_USERNAME" }
| { outcome: "SUCCEEDED" }






Delta/Novo estado: objeto contendo os valores derivados (ex.: passwordHash, normalizedEmail, objetos VO prontos). Pode ser:




  • O objeto completo que representa a entidade pronta para criar, ou

  • Apenas as mudanças (delta) para aplicar ao estado atual.



Ambas formas são puras.



A partir disso, a Factory, uma função pura, recebe os dados validados/derivados (delta) e monta a entidade/VO final de forma imutável.






O Orquestrador entre Domínio e Infraestrutura



Após entendermos que o Deriver decide as regras de negócio puras, precisamos de uma camada que orquestre o caso de uso. É aqui que entra o Handler (às vezes também chamado de Use Case ou Shell).



O Handler não contém regras de negócio profundas: ele coordena o fluxo de uma operação. Ele recebe um DTO já validado, chama o Deriver para obter um Outcome e, dependendo desse resultado, decide o próximo passo.



Se o Deriver retornar uma falha, o Handler devolve essa falha diretamente para o controller. Se retornar um “SUCCEEDED” com um delta/novo estado, então o Handler chama a Factory para montar a entidade final e delega ao repositório o trabalho de persistência.



Todo esse conhecimento adquirido se traduziu neste projeto simples, onde busquei seguir um modelo de arquitetura Package by Feature, fazendo algumas adaptações para o cenário no qual estou mais acostumado. Para entender mais da arquitetura, deixo o ótimo artigo do Waldemar Neto que serve como guia e que eu utilizei como base para desenvolver meu projeto.



Aos poucos vou me aprofundando em TypeScript e programação funcional, mas esse primeiro passo foi bem divertido e já me mostra ótimas possibilidades para o futuro.



Referências:



Arquiteturas Emergentes Que Você Precisa Conhecer: Package by Feature, Vertical Slice e Modular

Domain-driven design in functional programming

Domain-Driven Design (DDD) com Programação Funcional?!?!

Functional Domain Driven Design: Simplified

Domain Driven Design implemented by functional programming

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Uma Abordagem Funcional para Domain-Driven Design

Thematisch verwandte Begriffe: Abordagem, Funcional, para, DomainDriven · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

💬 Kommentare werden geladen…
Zum Aktualisieren ziehen
Nächster Beitrag