DDD: Dúvida sobre modelagem de um dominio

Olá eu tenho o seguinte modelo relacional:

onde eu tenho uma generalização, ou seja vou ter diferentes tipos de usuários que possuem alguns atributos em comum.

Mas na hora de passar isso para o dominio eu estou com dúvida…

O meu aggregrado deveria se chamar Person ou User, ou Students seria um aggregrado, teacher outro aggregrado ?

Nesse contexto a minha tabela user é apenas a conta da pessoa, eu deveria considerar como uma entidade que faz parte do aggregrado ou como um value object?

Eu deveria ter uma classe base com nome PERSON, ou eu posso ignorar isso e repetir dados em cada aggregrado ( students, teacher, etc).

Para evitar possíveis contratempos, aconselho tu repetir os dados em comum nas entidades, e manteria “Person” como uma interface declarando (nessa interface) apenas os métodos getters comum à pessoa (evite usar herança nesse seu cenário).

Sobre o que deve ser um aggregado root ou não, tente pensar assim: se faz sentido tal coisa existir sem a outra. Se fizer sentido, provavelmente essa coisa será um aggregate root.

1 curtida

é realmente eu fiz assim, o que mais pegou é na parte do usuário de uma pessoa, eu tou na dúvida se devo considerar como uma entidade ou apenas um objeto de valor, eu imagino que ela faça parte dos aggregrados: students, teacher.

Mas eu não sei se por isso acontecer devo considerar usar apenas user como entidade e associar a esses aggregados eu se devo considerar como parte desses aggregrados, até por que um usuário n existiria sem uma pessoa.

Eu penso que um usuário de uma pessoa deva ser um objeto de valor, mas o que me deixa com dúvida é por que um objeto de valor é imutável, e eu tenho um field chamado refresh_token que eu sempre vou alterar de acordo com a expiração desse token, eu não sei se isso tornar mutável.

Oque você acha desse ponto?

Vi que a relação entre usuário e pessoa é: Um usuário possui uma pessoa. O mais lógico não seria uma pessoa possuir um usuário?

Imagina vc como pessoa sendo utilizador de um sistema como o GUJ, por exemplo. De cara, vc já teria que ter um usuário para poder usar o sistema.

1 curtida

Não pensei nesse ponto, realmente fiquei na dúvida agora, eu pensei que dessa forma seria isso: uma pessoa possui um usuário, pq com essa modelagem um usuário só poderia pertencer a uma pessoa, ( e uma pessoa poderia ter mais de um usuário, que não é o objetivo, um usuário pode ter apenas uma pessoa)

você então acha que o melhor seria um user_id na tabela person ?

mas dessa maneira você acha que o usuário de uma pessoa seria um objeto de valor ou uma entidade ?

Eu penso que um usuário de uma pessoa deva ser um objeto de valor, mas o que me deixa com dúvida é por que um objeto de valor é imutável, e eu tenho um field chamado refresh_token que eu sempre vou alterar de acordo com a expiração desse token, eu não sei se isso tornar mutável.

Oque você acha desse ponto?

Na real, seu modelo está correto. Acho que eu que viajei aqui mesmo. =)

Como um usuário provavelmente terá um identificador, acredito que deveria ser uma entidade mesmo (pois value objects não possui identidade).

1 curtida

simmm, então eu penso que não existe sentido dela existir sem uma pessoa, e então creio que seja parte do aggregrado pessoa ?

e outra coisa que me deixa com dúvida kk da maneira eu pensei em criar as seguintes pastas:

students
teachers

que são as generalizações de pessoas, mas eu teria que repetir essa entidade user em cada pasta ou você acha mais coerente criar um modulo para o user? eu fico confuso pq como ele faz parte desse aggregrado ele não deve ter repository.

1 curtida

Eu estava pensando exatamente nisso aqui. Pelo que acho, o mais lógico seria replicar o usuário para cada agregado em que ele faz parte, mas tb acho meio estranho fazer isso nesse caso.

Pensando no domínio, acredito que faça sentido manipular um estudante ou professor sem depender de um usuário. Tipo, o usuário provavelmente será usado apenas em questões de login e talz.

Pensando assim, acredito que Pessoa possa ser um agregado que e usuário faça parte, e estudante e professor também sejam agregados separados.

> dominio
    - (Agg) pessoa
        - usuário (não faz sentido ter um usuário sem uma pessoa)
    - (Agg) estudante
    - (Agg) professor

Nos outros agregados, tu pode manter a referência da pessoa usando um domainId (ex.: PessoaId)


O que vc acha? (se bem que talvez seja o que tu estava planejando fazer jah)

1 curtida

Realmente, gostei, creio que seja a melhor opção, ter o usuário apenas no contexto do aggregrado pessoa.
Cara muito obrigado kkk, eu tava quebrando cabeça com isso, agora ficou bem mais claro, eu estava com medo dessa repetição de dados.

1 curtida

@Lucas_Camara você poderia tirar só mais essa dúvida?
Como user faz partr do aggregrado pessoa, as funções de por exemplo, validar accesstoken ou algo do tipo devem está presente no aggregate não é?

Quantas dúvidas tu quiser. Isso eh bom pra mim tb para exercitar o conceito.

Sim. A ideia que penso é a camada de aplicação perguntar para a camada de dominio, no agregado de pessoa, se tal pessoa está com usuário válido.

Porém tem um detalhe. Na validação do usuário, a parte que valida o token talvez esteja mais mais para um detalhe de infraestrutura, ou seja, a verificação do refresh token capaz que é feita através de uma lib jwt, certo? Se for, vc deve manter esse lógica de validação na camada de infraestrutura.

1 curtida

Obrigado @Lucas_Camara
Surgiu mais uma dúvida kk, agora com relação a validação de fields, eu acabei lendo bastante artigos que falam que o ideal é as validações de fields nas entidades.

E então eu acho que realmente é algo valido, eu pensei em fazer algo por composição ou apenas receber a validação como params da função da entidade.
Então eu teria no dominio alguma pasta chamada de Validators.
Ou então validar inputs no command ( no uso do cqs ou cqrs ) ?

Outra questão é eu vi em algum lugar que não é para fazer persistências no dominio, mas e caso esse validator faça alguma consulta, é algo que a se evitar?

As validações servem para vc garantir a integridade dos dados em relação às regras do domínio. Então, se tu tiver uma operação que altera o email do estudante, por exemplo, penso que vc teria algo mais ou menos assim:

Dominio

// Aggregate Root
public class Estudante {
	private EstudanteId id;
	private String email;
	//demais atributos
	
	Estudante() {}
	
	public void registrarEmail(String novoEmail) {
		validarEmail(novoEmail);
		this.email = novoEmail;
	}
	
	private validarEmail(String novoEmail) {
		boolean valido = //aqui vc valida se o e-mail foi preenchido e se está no formato válido
		
		if (!emailValido) {
			throw new DominioException("O email \"" + novoEmail + "\" não é válido.");
		}
	}
}

No caso da criação de uma novo estudante (seguindo o mesmo exemplo), vc pode validar todos os campos necessários para garantir a integridade do estudante num construtor, ou num método construtor, ou numa classe factory (factories são indicadas quando a construção do objeto é mais complexo).

Externalizar as validações numa classe “Validators”, por exemplo, pode ficar bom. Apenas mantenha essa classe de validação na camada de dominio e de acordo com o agregado em que ela se refere.

Boa pergunta. Toda chamada à camada de persistencia deve ser feita na camada de aplicação. Caso algum validador precise consultar algum repositorio (seja do proprio aggregate ou de outro), então é bem capaz que essa validação tenha que ficar num serviço de domínio.

Imagine que vc tenha que gravar um estudante e que vc tenha que verificar se já exista um mesmo e-mail cadastrado no banco. Nesse caso, usar um serviço de domínio é o ideal. A abordagem que gosto é passar o serviço como parâmetro num método do agregado e chamar a validação de lá.

Serviço do domínio do estudante

public class EstudanteService {
	private final EstudanteRepository estudanteRepository;
	
	public EstudanteService(EstudanteRepository estudanteRepository) {
		this.estudanteRepository = estudanteRepository;
	}

	public boolean seEmailJaExiste(String email) {
		boolean existe = estudanteRepository.verificarSeEmailExiste(email);
		return existe; // ou tu pode lançar um exceção em vez de um boolean, fica a seu criterio
	}
}

Aggregate Root

public class Estudante {
	private EstudanteId id;
	private String email;
	//demais atributos
	
	Estudante() {}
	
	public void registrarEmail(String novoEmail) {
		validarEmail(novoEmail);
		this.email = novoEmail;
	}
	
	private validarEmail(String novoEmail, EstudanteService estudanteService) {
		boolean valido = //aqui vc valida se o e-mail foi preenchido e se está no formato válido
		
		if (!emailValido) {
			throw new DominioException("O email \"" + novoEmail + "\" não é válido.");
		}
		
		if (estudanteService.seEmailJaExiste(novoEmail)) {
			throw new DominioException("O email \"" + novoEmail + "\" já existe cadastrado no sistema.");
		}
	}
}

Uma observação importante é se o serviço de domínio possuir algum aspecto de infraestrutura, é melhor tirá-lo do domínio e colocá-lo em infraestrutura mesmo. E no domínio apenas usar uma interface implementada pela classe do serviço desse domínio, assim:

public interface EstudanteService {
    // ...
}
public class EstudanteServiceImpl implements EstudanteService {
	
	private OutlookExternalEmailValidation servico; //exemplo de serviço externo que não tem nada a ver com o domínio
	
	// ...
}

Com isso, na camada de aplicação, vc poderia fazer algo assim:

public class AlterarEmailAppService {

	// supondo que vc esteja recebendo estudanteRepository e estudanteService por injeção
	private EstudanteRepository estudanteRepository;
	private EstudanteService estudanteService;

	public void registrarEmail(EstudanteId idEstudante, String novoEmail) {
		Estudante estudante = estudanteRepository.findById(idEstudante);
		estudante.registrarEmail(novoEmail, estudanteService);
	}
}
1 curtida

eu estava fazendo algo bem parecido com isso, me parece fazer mais sentido, e essa parte de validar Email eu vou deixar para o aggregrado Person, seria o ideal ?

obrigado man.

A pessoa vai ser responsável por criar o usuário. Ou seja, em algum momento vc terá um new User() em algum canto no agregado de pessoa. Com isso, vc pode deixar a validação dentro da classe User mesmo (seja no construtor ou algum método de construção).

1 curtida

Opa @Lucas_Camara , boa noite kkk

cara surgiu algumas dúvidas com relação a authentication, authroization
básicamente eu vou ter “roles” de acordo com o tipo do usuário, ex professor pode fazer fazer algumas coisas que “estudante” não pode no sistema.
E eu li em algum canto que não é interessante levar Roles para o contexto do dominio, mas ai eu fiquei perdido em como fazer isso kk.

E basicamente coisas como comparar uma senha em plaintext com uma senha do banco, e fazer logout, são coisas que devem está ligadas ao meu dominio, a minha entidade person??

e seguindo aquela ideia de person, teacher, student eu fiz a entidade person:

export class Person extends Entity<IPersonProps> {

  private constructor(props: IPersonProps, id?: string) {

    super(props, id)

  }

  public get credentials(): Credentials {

    return this.props.credentials

  }

  public get name(): PersonName {

    return this.props.name

  }

  public get email(): PersonEmail {

    return this.props.credentials.props.email

  }

  public static build(props: IPersonProps & { id?: string }): Person {

    if(!props) throw new NullExecptionError()

    const person = new Person(props, props.id)

    return person

  }

  public createUser(data: Credentials) {

    if (this.props.credentials) throw new Error(`invalid operation`)

    this.props.credentials = data

  }

}

e então seguindo aquela logica a minha entidade student teria uma referência da entidade person no aggregrado de estudante, pq estudante é uma pessoa??

algo como:

export class Student extends Entity<{Person: Person}> {

  private constructor(props: {Person: Person}, id?: string) {

    super(props, id)

  }

  public get person(): Person {

    return this.props.Person

  }

  //and her student methods

}

Penso que essa parte de autenticação e autorização deveria ficar na parte de infraestrutura, pois se trata de um detalhe tecnico. Vc pode ter serviços de infraestrutura, como um AutenticacaoService, que iria expor métodos para verificar se tal usuário tem acesso à determinada role, assim como efetuar login e logout, por exemplo.

Ex.: Se vc for autenticar um professor:

import autenticacaoService from './infraestrutrura/autenticacao';

const idProfessor = // recebe o id do professor de alguma forma
const senha = // recebe a senha de alguma forma
const token = autenticacaoService.autenticar(idProfessor, senha);

Um exemplo tosco, mas para tentar passar a ideia.

realmente eu também assim, e com relação a associação do person na entidade student, me pareceu o caminho correto, iria fazer o mesmo com o teacher e outros usuários do sistema, você acha que está certo?

Ou simplesmente repetir as props, objetos de valor etc etc, novamente ou apenas pegar um id de person e repetir os objetos de valores etc etc

Cara, gostei da abordagem que tu fez. Porém, replicar os campos também não estaria errado. Trata-se apenas de uma decisão de arquitetura que vc achar melhor para a manutenibilidade do código.

1 curtida