Separation of Concerns (SoC) entre Services and Entities

Olá, tenho algumas dúvidas sobre onde colocar certas operações dentro do meu sistema. Por exemplo no atual sistema tenho as seguintes classes:

class Transferencia{
//alguns atributos
private List<ItemTransferencia> itens;
}
class ItemTransferencia{
  //alguns atributos 
  private Produto produto;	
}

E neste sistema tenho a operação Transferencia, que é a de movimentar o estoque de uma FilialOrigem para uma FIlialDestino. Isso gravaria um LOG chamado MovEstoque, que uma outra entidade, gravando que fez, a hora que fez, etc.

Essa operação seria a responsabilidade de um ServiceTransferencia ou da propria entity Transferencia?

Se eu coloco as operacoes no Service, minhas classes acabando se tornando anêmicas, ou seja anti-pattern Anemic Domain Model, e se eu coloco a operacao na Entity acabando tendo que acessar Repositrios/Daos, e efetuar coisas como update, save, das quais acho estranho uma Entity ter.

Então de quem seria essa responsabilidade?

Obrigado

No service seria mais apropriado. Se eu fosse você, criaria uma camada de negócio (BO) para realizar estas operações. Seria um intermediário entre seu serviço e sua camada de persistência.

Olá, BO é uma má prática, você separa os comportamentos de um objeto de seus atributos, transformando sua programacao em procedural.

Fonte:
http://www.fragmental.com.br/wiki/index.php?title=Evitando_VOs_e_BOs

Olá, BO é uma má prática, você separa os comportamentos de um objeto de seus atributos, transformando sua programacao em procedural.

Fonte:
http://www.fragmental.com.br/wiki/index.php?title=Evitando_VOs_e_BOs

[/quote]

Só uma má prática se vc implementar errado. BO agora se chamam Domain Objects e ninguem acha errado. mas são a mesma prática.

Service é realmente a solução. Pense assim, se vc quer mover uma caixa de um quarto para o outro quem a move ? a caixa se move ?
o quarto as faz mover ? Não. Você faz isso. O que vc é ? Um serviço de mudanças.

Olá, BO é uma má prática, você separa os comportamentos de um objeto de seus atributos, transformando sua programacao em procedural.

Fonte:
http://www.fragmental.com.br/wiki/index.php?title=Evitando_VOs_e_BOs

[/quote]

Só uma má prática se vc implementar errado. BO agora se chamam Domain Objects e ninguem acha errado. mas são a mesma prática.

Service é realmente a solução. Pense assim, se vc quer mover uma caixa de um quarto para o outro quem a move ? a caixa se move ?
o quarto as faz mover ? Não. Você faz isso. O que vc é ? Um serviço de mudanças.

[/quote]

Olá sérgio, mas no meu caso eu tenho a classe MovimentoEstoque, da qual tem os atributos necessários para gerar o log de movimentação do estoque.
Neste caso a responsabilidade não seria da Própria classe MovimentoEstoque porque senão a classe MovimentoEstoque ficaria anêmica seria apenas um agrupamento de dados.

Obrigado

Olá, BO é uma má prática, você separa os comportamentos de um objeto de seus atributos, transformando sua programacao em procedural.

Fonte:
http://www.fragmental.com.br/wiki/index.php?title=Evitando_VOs_e_BOs

[/quote]

Só uma má prática se vc implementar errado. BO agora se chamam Domain Objects e ninguem acha errado. mas são a mesma prática.

Service é realmente a solução. Pense assim, se vc quer mover uma caixa de um quarto para o outro quem a move ? a caixa se move ?
o quarto as faz mover ? Não. Você faz isso. O que vc é ? Um serviço de mudanças.

[/quote]

Olá sérgio, mas no meu caso eu tenho a classe MovimentoEstoque, da qual tem os atributos necessários para gerar o log de movimentação do estoque.
Neste caso a responsabilidade não seria da Própria classe MovimentoEstoque porque senão a classe MovimentoEstoque ficaria anêmica seria apenas um agrupamento de dados.

[/quote]

E qual é o problema em ser um agrupamento de dados se for essa a responsabilidade da classe ? Repare que essa classe apenas contém dados porque ela é basicamente um artefacto de log. vc mesmo disse que ela tem os atributos necessários para gerar o log.

então o seu serviço move X de A para B e cria um objecto M que contém informações dessa movimentação. É o mesmo que vc faria se fosse de um seriço de mudanças. vc tem que registrar que transportou as coisas ou ninguem lhe paga ou pior, ninguem acredita que vc fez o serviço.

Acho que vc tem que se resolver com essa coisas do anémico. Mas se isso o confundo esqueça isso por um momento e pense em OO. Pense em SoC.
(afinal é o tema do post)

Olá Sérgio, então eu estou pensando exatamente em OO. Pois se deixo minhas classes como agrupamento de dados, elas se tornam procedural longe de serem OO.

Aproveitando pego o exemlo de uma classe Usuario ela tem em seus metodos alterarSenha(senhaVelha,senNova),
isso e uma responsabilidade da propria classe, agora te pergunto ainda assim haveria a necessidade de um serviço somente para gravar no banco as alterações ou a propria gravacao do banco seria tbm responsabilidade da propria classe Usuario invococando por exemplo um RepositoryUsuario.

Obrigado

[quote=danielbussade] Olá Sérgio, então eu estou pensando exatamente em OO. Pois se deixo minhas classes como agrupamento de dados, elas se tornam procedural longe de serem OO.

Aproveitando pego o exemlo de uma classe Usuario ela tem em seus metodos alterarSenha(senhaVelha,senNova),
isso e uma responsabilidade da propria classe, agora te pergunto ainda assim haveria a necessidade de um serviço somente para gravar no banco as alterações ou a propria gravacao do banco seria tbm responsabilidade da propria classe Usuario invococando por exemplo um RepositoryUsuario.

[/quote]

O que posso dizer é que você está equivocado.
Você acha que ser OO é colocar um monte de métodos nos objetos. Isso não é verdade.
Você tem que saber separar a responsabilidade. E isso não é uma questão de objetos é uma questão de modelo.
Se as suas classes são agrupamentos de dados e é isso que elas devem ser, então que sejam. colocar métodos artificiais só para que elas “tenham alguma coisa que fazer” é que é o erro.

Deixar dados serem dados não é transformas a sua programação em procedural. Isso é um mito que vc inventou na sua cabeça.

O seu exemplo do usuário é muito bom. Investigue API como o Spring Security para ver se existe isso no objeto que representa o usuário. Não existe. E a razão é simples : essa responsabilidade não é do usuário.

Não é o usuário que altera a senha é o sistema. Se é o sistema, então é um serviço que faz isso.
Muitas verificações podem ser necessárias e as regras podem mudar . isso é sinal de que é coisa para um serviço fazer e não para a classe.

Veja assim : uma classe de entidade é responsável por manter e controlar a alteração do seu próprio estado. Não do estado do sistema ou das outras classes. Isso é coisa para serviços fazerem.

Pela sua logica a classe String deveria ter um método gravaNoBanco() já que a todo o momento strings são gravadas em banco de dados. Não isso é errado. String tem métodos que dizem respeito a Strings. E pronto.

Você está seguindo um mito e deixando de lado a separação de responsabilidade.
A sua dúvida já foi respondida. A responsabilidade que vc quer é de um serviço.
Agora ou vc entende isso, ou continua perseguindo um mito.

A escolha é sua.

Como você mesmo disse manter e controlar alteração do seu proprio estado. O estado do objeto usuario compreende um login e senha, entao nao seria uma alteração do proprio objeto.

Caso não , exemplifique algum metodo de responsabilidade de uma classe usuario, um metodo que seja de negocio.

[quote=sergiotaborda]Não é o usuário que altera a senha é o sistema. Se é o sistema, então é um serviço que faz isso.
Muitas verificações podem ser necessárias e as regras podem mudar . isso é sinal de que é coisa para um serviço fazer e não para a classe.
[/quote]

Se eu for seguir essa regra, tendo um objeto carro um metodo acelera() nao deveria estar na classe carro e sim em um Service visto que o carro não se acelera, quem faz isso eh o motorista;

Acho que devemos abstrair um pouco, vc diz que o Sistema é quem faz isso, mas o sistema é formado pelo conjunto das classes, nao existe uma classe Sistema. Por isso ainda acho que esse metodo e de responsabilidade do Usuario.

[quote=danielbussade] Olá Sérgio, então eu estou pensando exatamente em OO. Pois se deixo minhas classes como agrupamento de dados, elas se tornam procedural longe de serem OO.

Aproveitando pego o exemlo de uma classe Usuario ela tem em seus metodos alterarSenha(senhaVelha,senNova),
isso e uma responsabilidade da propria classe, agora te pergunto ainda assim haveria a necessidade de um serviço somente para gravar no banco as alterações ou a propria gravacao do banco seria tbm responsabilidade da propria classe Usuario invococando por exemplo um RepositoryUsuario.

Obrigado[/quote]

No exemplo do usuario pode ser das duas formas. Ei prefiro um service e deixar usuario sem saber nada sobre gravacao.

No caso do estoque, o Sergio tem razao, sua classe MovimentoEsqtoque só serve de log, como voce disse, entao ela nao precisa de comportamento.

O que eu estou estranhando no seu modelo é, o que voce chamou de Entity, Transferencia. Ela vai ser persistida, se sim, por que voce precisa de log?

Quanto a onde de fato fazer a transferencia é uma decisao nao tao simples mesmo. Voce vai ter um Estoque pertencente a filial A e um Estoque pertencendo a Filial B. Um coisa é certa, voce nao vai fazer estoque.setQuantidade(novaQuantidade). Voce tera estoqueFilialB.aumentar(quantidade) e estoqueFilialA.diminuir(quantidade). A duvida agora quem vai chamar os metodos aumentar e diminuir dos Estoques.
'
O que o Sergio está sugerindo (eu acho) é que um servico de transferencia “pegue” os estoques das filiais “setadas” na transferencia e as quantidades e chame os metodos aumentar e diminuir. Já eu prefiro, num primeiro momento, que o objeto transferencia seja o responsavel pela (guess?) transferencia.

Eu disse num primeiro momento pq vc deve ter cuidado com o acoplamento entre Transferencia e um possivel (nao conheco seu problema) subsistema de estoque e inventario das filiais. Buscar o estoque de cada produto nao pode estar acoplado a Transferencia e um metodo transferencia.transferir() pode se tornar muito complexo e por consequencia dificil de testar, o que é muito ruim.

Entao analise o seu problema e veja o que é mais simples.

[quote=YvGa][quote=danielbussade] Olá Sérgio, então eu estou pensando exatamente em OO. Pois se deixo minhas classes como agrupamento de dados, elas se tornam procedural longe de serem OO.

Aproveitando pego o exemlo de uma classe Usuario ela tem em seus metodos alterarSenha(senhaVelha,senNova),
isso e uma responsabilidade da propria classe, agora te pergunto ainda assim haveria a necessidade de um serviço somente para gravar no banco as alterações ou a propria gravacao do banco seria tbm responsabilidade da propria classe Usuario invococando por exemplo um RepositoryUsuario.

Obrigado[/quote]

No exemplo do usuario pode ser das duas formas. Ei prefiro um service e deixar usuario sem saber nada sobre gravacao.

No caso do estoque, o Sergio tem razao, sua classe MovimentoEsqtoque só serve de log, como voce disse, entao ela nao precisa de comportamento.

O que eu estou estranhando no seu modelo é, o que voce chamou de Entity, Transferencia. Ela vai ser persistida, se sim, por que voce precisa de log?

Quanto a onde de fato fazer a transferencia é uma decisao nao tao simples mesmo. Voce vai ter um Estoque pertencente a filial A e um Estoque pertencendo a Filial B. Um coisa é certa, voce nao vai fazer estoque.setQuantidade(novaQuantidade). Voce tera estoqueFilialB.aumentar(quantidade) e estoqueFilialA.diminuir(quantidade). A duvida agora quem vai chamar os metodos aumentar e diminuir dos Estoques.
'
O que o Sergio está sugerindo (eu acho) é que um servico de transferencia “pegue” os estoques das filiais “setadas” na transferencia e as quantidades e chame os metodos aumentar e diminuir. Já eu prefiro, num primeiro momento, que o objeto transferencia seja o responsavel pela (guess?) transferencia.

Eu disse num primeiro momento pq vc deve ter cuidado com o acoplamento entre Transferencia e um possivel (nao conheco seu problema) subsistema de estoque e inventario das filiais. Buscar o estoque de cada produto nao pode estar acoplado a Transferencia e um metodo transferencia.transferir() pode se tornar muito complexo e por consequencia dificil de testar, o que é muito ruim.

Entao analise o seu problema e veja o que é mais simples.[/quote]

Ola muito bom seu post. Então o sistema funciona exatamente assim o estoque e separado por filial, entao tenho a classe Estoque onde tem Filial e Produto.

Ai digamos que queira o estoque do produto x, na classe produto tenho o metodo getEstoque(Filial filial), onde retorno a quantidade em estoque naquela filial, tbm no produto tenho os metodos

public void diminuiQuantidadeEmEstoqueNaFilial(Filial filial,Estoque estoque,float quantidadeASerDiminuida)throws BusinessException{
		float quantidadeEmEstoque=0;
		float quantidadeAtualizada=0;
		quantidadeEmEstoque=estoque.getQuantidade();
		
		if(quantidadeEmEstoque >= quantidadeASerDiminuida){
			quantidadeAtualizada=quantidadeEmEstoque - quantidadeASerDiminuida;
			setQuantidadeEmEstoqueNaFilial(filial,estoque,quantidadeAtualizada);
		}else{
			throw new BusinessException("Estoque insuficiente!" + "\n" + 
										"Estoque atual: "+ quantidadeEmEstoque + "\n" + 
										"Quantidade movimentada: " + quantidadeASerDiminuida);
		}
	}
	
	public void aumentaQuantidadeEmEstoqueNaFilial(Filial filial,Estoque estoque,float quantidadeASerAumentada){
		float quantidadeEmEstoque=0;
		float quantidadeASerAtualizada=0;
		quantidadeEmEstoque=estoque.getQuantidade();
		quantidadeASerAtualizada=quantidadeEmEstoque + quantidadeASerAumentada;
		setQuantidadeEmEstoqueNaFilial(filial,estoque, quantidadeASerAtualizada);
	}
	
	public void trocaQuantidadeEmEstoqueNaFilial(Filial filial,Estoque estoque,float quantidadeASerTrocada){
		setQuantidadeEmEstoqueNaFilial(filial,estoque, quantidadeASerTrocada);
	}

A unica coisa ruim deste metodo e que tive que passar o estoque como parametro embora o produto tenha o metodo getEstoque(Filial filial) no meu service eu preciso ter a referencia a estoque para dar um repositorio.save(estoque), ou seja eu ja recuperei o estoque se eu nao passo ele como parametro teria que executar a pesquisa duas vezes.

A questao da transferencia e complicada porque existem duas telas no sistema a tela de MovEstoque onde o usuario utiliza para corrigir estoque, fazer inventario, entrada de produto avulso, e a tela de transferencia que tbm gera movEstoque, mas funciona da seguinte forma:

Filial A transfere 10 produto para Filial B

O sistema retira os 10 da filial A na mesma hora, mas nao coloca os 10 na B na mesma hora
Precisa do outro lado a filial B efetivar ai sim e inserido os 10 na Filial B;

Caso a filial B nao efetive somente podera ser cancelada essa transferencia pela filial.

Todas duas gerariam o MovEstoque uma com operacao de Entrada de Transferencia
e outra com Saida de Transferencia, por isso preciso do MovEstoque.

Entendeu?

IMHO acredito que não há problema em existirem classes que irão “dar a partida” em algumas atividades ou que executem algum serviço como um log por exemplo.
O caso é apenas definir em qual classe faz mas sentido estar cada método. O que pela divergência de opiniões mostra que nem sempre é simples. :slight_smile:
Mas tenho algumas opiniões sobre o que foi dito:

Para alterar a senha através da própria classe esta precisaria tomar conhecimento da camada responsável pela persistência. Não acho isso muito bom a menos que vc esteja usando algo como ActiveRecord.

Acho que o usuário poderia ser responsável por exemplo por verificar se a senha que foi passada confere: usuario.isSenhaValida(String senha), se possui determinada permissão de acesso: usuario.temPermissao(Permissao permissao), ou então se ele é um administrador: usuario.isAdministrador(). É claro que isso tudo vai depender totalmente do contexto de cada projeto.

Mas eu tb tenho muitas dúvidas com relação a essas questões de responsabilidade. Em geral, passado algum tempo após construir uma classe de serviço, percebo que muitas das responsabilidades atribuídas a ela ficariam (com pequenas mudanças) muito melhor em classes do domínio. Então é hora de refatorar! :smiley:

[]'s
Fred

Para você ser marceneiro você tem que saber o que é madeira. Para vc trabalhar com OO, vc tem que saber SoC.
Senão vc não está fazendo OO. Este é o ponto.

Sim, muita gente tem duvidas quanto a SoC. Mas esse é o tipo de coisa difícil de explicar no forum. Vamos então com algumas coisas básicas.

O que é o básico do SoC ? Cada classe deve ter o minimo de responsabilidade possivel, mas não menos.

Se eu cozinho sou cozinheiro, se vendo, vendedor, se como, comilão :wink: se choro, chorão … se valido ?
Validador!!!

Validações são feitas por validadores! Não por serviços. Não por entidades. Não por DAO, não por nada que não tenha a única e exclusiva responsabilidade de validar.

isSenhaValida é um caso para um validador de senha. “Senha” , pasmem-se, é uma entidade. Ela só não é um agregado.

Então aqui já temos : Senha e Validador

Mudar se senha… humm… mudar de senha é um assunto delicado. O que acontece se algum passo der errado ?
hummm… isso soa a transação… é! transação. E transação é coisa para serviços.

Usuários têm senhas, não são senhas. Senhas não são atributos do usuário, são associadas a usuários. ( um atributo é associado, mas algo associado, não é necessariamente um atributo)

Um usuário pode ter mais do que uma senha conforme o que a senha protege. É para isso que existe o single sign on (uma senha to rule them all => existe mais que uma).

Usuário.temPermissão(paraLevarTodoOdinheiroDoBancoParaCasa) ? O que vc acha que ele irá responder ?

Verificação de permissões é feita por um agente de segurança ( um Guardian), não pelo o usuário. O modelo é o seguinte: o sistema tem que ser protegido. Para isso existem agentes de segurança que autenticam e autorizam (ou não) o usuário. Vários agentes, várias permissões, um usuário.

Em um banco, A compra ações de B. Quem faz a transação ? o Broker. Quando existe um terceiro cara isso significa : serviço. (Serviço de Broker , ou Broking. Já ouviu falar?)

Qual é a responsabilidade de um entidade ? Ter identidade. São simples quanto isso. Para isso ela tem que ser criada. Aqui que complica. Manter o estado do objeto é simples (get/set) mas e manter o estado do sistema ? posso dar set em qualquer objeto a qualquer hora ? Não. Isso é delicado. É coisa para alguem responsável. Mais responsável que a entidade : o serviço.

Quem pinta é pintor, quem constrói é … ?
Construtor.

O padrão Builder é para criar objetos ( entidade é um caso particular de objeto) com estado consistente quando usar set não é atómico o suficiente ( ou seja, vc precisaria setar duas ou mais coisas simultaneamente e isso , simplesmente, é impossível com set)

Métodos “set” são Modificadores. Isso significa que estão mudando algo. Isso significa que eles não criam nada, apenas alteram. Para criar é preciso usar construtores ou outros objetos como Builder.
(é por isso que injeção via método set é furada na certa. só viável em caso em que a dependência é opcional.)

produto.alteraQuantidadeEstoque ( 20) … hummm… e quando o produto está em rota dentro de um caminhão. O que esse método faz ? manda um sms para o armazém ?

Produto tem um estoque ? Não. Estoque tem produtos.

Estoque.alteraQuantidade(Produto, 20) seria melhor. Melhor ainda :

Quantidade<Produto> quantidadeTransferida1 = estoqueA.remove(produto, 20);

estoqueB.adiciona(quantidadeTransferida1);
Transferencia transferencia1 = Transferencia.nova(estoqueA, estoqueB, quantidadeTransferida1 , relogio.agora());

Logger.log(transferencia1);

//diferente de:

Quantidade<Produto> quantidadeTransferida2 = estoqueB.remove(produto, 20);

estoqueA.adiciona(quantidadeTransferida2);
Transferencia transferencia2 = Transferencia.nova(estoqueB, estoqueA, quantidadeTransferida2 , relogio.agora());

Logger.log(transferencia2);

ups…enganei-me


Transferencia estorno = transferencia2.reverse(); // transferencias sabem como seria a sua "anti-transferencia. isto é um método que não altera dados, cria outra coisa, mas não precisa ser transacional.

Logger.log(estorno);

Quantidade<Produto> quantidadeTransferida3 = estoqueB.remove(produto, 40);

estoqueA.adiciona(quantidadeTransferida3);
Transferencia correto = Transferencia.nova(estoqueB, estoqueA, quantidadeTransferida3 , relogio.agora());

Logger.log(correto );

O assunto é vasto e complexo. Isto é só uma base.

Ah! e Validação é feita por Validadores. Objetos são responsáveis pela sua Consistência.
Validação != Consistência.

[quote=sergiotaborda]Para você ser marceneiro você tem que saber o que é madeira. Para vc trabalhar com OO, vc tem que saber SoC.
Senão vc não está fazendo OO. Este é o ponto.

Sim, muita gente tem duvidas quanto a SoC. Mas esse é o tipo de coisa difícil de explicar no forum. Vamos então com algumas coisas básicas.

O que é o básico do SoC ? Cada classe deve ter o minimo de responsabilidade possivel, mas não menos.

Se eu cozinho sou cozinheiro, se vendo, vendedor, se como, comilão :wink: se choro, chorão … se valido ?
Validador!!!

Validações são feitas por validadores! Não por serviços. Não por entidades. Não por DAO, não por nada que não tenha a única e exclusiva responsabilidade de validar.

isSenhaValida é um caso para um validador de senha. “Senha” , pasmem-se, é uma entidade. Ela só não é um agregado.

Então aqui já temos : Senha e Validador

Mudar se senha… humm… mudar de senha é um assunto delicado. O que acontece se algum passo der errado ?
hummm… isso soa a transação… é! transação. E transação é coisa para serviços.

Usuários têm senhas, não são senhas. Senhas não são atributos do usuário, são associadas a usuários. ( um atributo é associado, mas algo associado, não é necessariamente um atributo)

Um usuário pode ter mais do que uma senha conforme o que a senha protege. É para isso que existe o single sign on (uma senha to rule them all => existe mais que uma).

Usuário.temPermissão(paraLevarTodoOdinheiroDoBancoParaCasa) ? O que vc acha que ele irá responder ?

Verificação de permissões é feita por um agente de segurança ( um Guardian), não pelo o usuário. O modelo é o seguinte: o sistema tem que ser protegido. Para isso existem agentes de segurança que autenticam e autorizam (ou não) o usuário. Vários agentes, várias permissões, um usuário.

Em um banco, A compra ações de B. Quem faz a transação ? o Broker. Quando existe um terceiro cara isso significa : serviço. (Serviço de Broker , ou Broking. Já ouviu falar?)

Qual é a responsabilidade de um entidade ? Ter identidade. São simples quanto isso. Para isso ela tem que ser criada. Aqui que complica. Manter o estado do objeto é simples (get/set) mas e manter o estado do sistema ? posso dar set em qualquer objeto a qualquer hora ? Não. Isso é delicado. É coisa para alguem responsável. Mais responsável que a entidade : o serviço.

Quem pinta é pintor, quem constrói é … ?
Construtor.

O padrão Builder é para criar objetos ( entidade é um caso particular de objeto) com estado consistente quando usar set não é atómico o suficiente ( ou seja, vc precisaria setar duas ou mais coisas simultaneamente e isso , simplesmente, é impossível com set)

Métodos “set” são Modificadores. Isso significa que estão mudando algo. Isso significa que eles não criam nada, apenas alteram. Para criar é preciso usar construtores ou outros objetos como Builder.
(é por isso que injeção via método set é furada na certa. só viável em caso em que a dependência é opcional.)

produto.alteraQuantidadeEstoque ( 20) … hummm… e quando o produto está em rota dentro de um caminhão. O que esse método faz ? manda um sms para o armazém ?

Produto tem um estoque ? Não. Estoque tem produtos.

Estoque.alteraQuantidade(Produto, 20) seria melhor. Melhor ainda :

Quantidade<Produto> quantidadeTransferida1 = estoqueA.remove(produto, 20);

estoqueB.adiciona(quantidadeTransferida1);
Transferencia transferencia1 = Transferencia.nova(estoqueA, estoqueB, quantidadeTransferida1 , relogio.agora());

Logger.log(transferencia1);

//diferente de:

Quantidade<Produto> quantidadeTransferida2 = estoqueB.remove(produto, 20);

estoqueA.adiciona(quantidadeTransferida2);
Transferencia transferencia2 = Transferencia.nova(estoqueB, estoqueA, quantidadeTransferida2 , relogio.agora());

Logger.log(transferencia2);

ups…enganei-me


Transferencia estorno = transferencia2.reverse(); // transferencias sabem como seria a sua "anti-transferencia. isto é um método que não altera dados, cria outra coisa, mas não precisa ser transacional.

Logger.log(estorno);

Quantidade<Produto> quantidadeTransferida3 = estoqueB.remove(produto, 40);

estoqueA.adiciona(quantidadeTransferida3);
Transferencia correto = Transferencia.nova(estoqueB, estoqueA, quantidadeTransferida3 , relogio.agora());

Logger.log(correto );

O assunto é vasto e complexo. Isto é só uma base.

Ah! e Validação é feita por Validadores. Objetos são responsáveis pela sua Consistência.
Validação != Consistência.

[/quote]

Muito interessante seu post, me deu novas idéias. Aproveitando o assunto SoC, minha dificuldade é separar as responsabilidades entre:

Action
Service
Entity

Tipo como uso JSF, e meus atributos são injetados diretamente logo apos o submit, já no seu estado consistente, visto que uso Validadores e Converters do proprio JSF , minhas Action acaba como sendo uma ApplicationLayer, ai pergunto deste modo seria responsabilidade da Action realizar pesquisas, entre outras coisas recuperar o usuario que esta logado, e deixar pro services as inserções e alteracoes no sistema?

E o service seria o responsavel por abrir fechar e commitar transacoes, seria feito da forma mais monotona mesmo, tipo:

FuncionarioService(){

  public void createFuncionario(Funcionario f)throw SaveException{
   Repository<Funcionario> funcionarioRepository=new RepositoryFactory().getFuncionarioRepostiory();
   funcionarioRepository.getSession.beginTransaction();
  try{
   repositorio.add(f);
  repositorio.getSession.getTransaction().commit();
  }catch(Exception e){
    repositorio.getSession().getTransaction.rollback();
    throw new SaveException(e,"Erro ao salvar o Funcionario");
}   
 
}

Seria responsabilidade do Service, abir, commitar, e fechar transacoes. Pois desta forma, eu consigo ter total controle do codigo, e da atomicidade das transacoes. O unico problema e que o codigo se torna repetitivo, e chato.

E por ultimo as entity ficariam responsaveis somente por consistencias do tipo:

Funcionario 
private int idade;

setIdade(){
 if(idade <0) 
  throw new IllegalArgumentException("Idade ilegal");
}
   
}

}

Neste contexto, se eu nao usar nenhum container de IOc, ou J2EE de controle de transacoes, onde ficariam as mesmas?

Obrigado!

Olá, Sérgio teria alguma bibliografia para recomendar faland sobre SoC?

Obrigado!

Não. A responsabilidade do Action é Controlar o processo de apresentação. Ele vai orqeuestrar o que aplicação tem que fazer para responder ao usuário, mas ele em si, não vai fazer nada. Vai apenas delegar.

Recurperar coisa, paginações, leituras => chamada a Repository ou Service
Fazer coisas, inicar processor, alterar informações, alterar o estado do sistema => chamada a Service.

No caso geral

1 Action -> 1 Service -> ( 0-N Services + 0-M Reposiotorios )

Poderia. Mas ele não é responsável do serviço em si mesmo. Isso é coisa para a aplicação fazer. Normalmente via AOP, ou algum tipo de proxy.
(Poder fazer isso é uma vantagem da definição do serviço que obriga interface + Implementação)

Sim, essa é responsabilidade das entidades. Mas a responsabilidade maior é garantir que têm identidade. Normalmente isso é feito via ID do banco, mas existem outras formas. Entidades têm que ter equals e hashcode bem definido. E alguma forma de obter a sua identidade, caso esta seja possivel abstrair em um objecto.

Entidades podem fazer operações sobre os dados que elas têm, ou sobre os seus agregados. O conceito de entidade agregada é que é importante.
Essa entidade “cabeça” é que vc normalmente procura e usa embora ela tenha muitas outras associadas a ela.
Por exemplo, Pedido tens itens associados e o Pedido por ter um getItemCount ou um getTotal() que opera em cima dos itens do pedido. Isso é que são métodos de dominio/negocio.

Por exemplo, Produto pode ter um tipoProduto ou categoriaProcuto, mas essas entidades não são importantes sozinhas, apenas na associação com o produto para o cliassificar. Os algoritmos irão utillizar esta info, mas sempre a partir do produto.

Action = Controlador de Apresentação : orquesta a resposta ao usuário ( humano ou não). invoca o dominio
Serviço : porta de entrada para o dominio e alterações do estado do sistema
Entiddade : objetos cujas instancias todas juntas representam o estado do sistema.
Reposiotrio : procura objetos das entidades mediante critérios relevantes.

[quote=danielbussade]Olá, Sérgio teria alguma bibliografia para recomendar faland sobre SoC?
[/quote]

Infelizmente o máximo que posso oferecer é uma página no meu blog

[quote=sergiotaborda][quote=danielbussade]Olá, Sérgio teria alguma bibliografia para recomendar faland sobre SoC?
[/quote]

Infelizmente o máximo que posso oferecer é uma página no meu blog[/quote]

Olá Sérgio, obrigado pelas indicações, e pelas respostas. Vou colar aqui um exemplo de codigo para lhe mostrar como estou trabalhando atualmente;

Vamos supor um cadastro de Funcionario onde a Entity Funcionario esta definida assim:

@Entity
@Table(name="FUNCIONARIO")
public class Funcionario {
 

	@Id()
	@GeneratedValue(strategy=GenerationType.SEQUENCE,generator="GEN_FUNC")
	@SequenceGenerator(name="GEN_FUNC",sequenceName="GEN_FUNC",initialValue=0,allocationSize=1)
	@Column(name="IDFUNCIONARIO")
	private Long id;
	 
	@Column(name="NOME", length=60)
	private String nome;
	
	@Column(name="LOGIN", length=30,unique=true)
	private String login;
	 
	@Column(name="SENHA", length=60)
	private String senha;
	
	@Column(name="DATAPRIMEIROACESSO")
	@Temporal(value=TemporalType.DATE)
	private Date dataPrimeiroAcesso;
	
	@Column(name="DATAULTIMOACESSO")
	@Temporal(value=TemporalType.DATE)
	private Date dataUltimoAcesso;

	@OneToOne
	@JoinColumn(name="IDPERFIL", referencedColumnName="IDPERFIL")
	private Perfil perfil;
	
	@OneToOne
	@JoinColumn(name="IDFILIAL", referencedColumnName="IDFILIAL")
	private Filial filial;
	
	@Type(type="true_false")
	private boolean ativo;
	

	public Funcionario() {
		this.dataPrimeiroAcesso=new Date();
		this.dataUltimoAcesso=new Date();
		this.ativo=true;
	}

//Getters e Setters omitidos

Como trabalho com JSF tenho minha classe FuncionarioMBean, que é um POJo gerenciado pelo JSF, onde a definicao fica assim:

public class FuncionarioBean extends BaseBean<Funcionario> {
	private Funcionario funcionario;
	private FuncionarioService funcionarioService;
	
	public FuncionarioBean() {
		funcionario=new Funcionario();
		funcionarioService=new FuncionarioService();
	}



	public void novo(){
		funcionario=new Funcionario();
		prepareAdicionar();
	}
	
	public void criar() {
		try {
			funcionarioService.criarFuncionario(funcionario);
		} catch (PersistenceException e) {
			//redirecioa para uma pagina de erro
			//efetue o LOG do erro
		}
	
		preparePesquisar();
	}
	
	public void atualizar(){
		funcionarioService.atualizarFuncionario(funcionario);
		limpar();
		preparePesquisar();	
	}
	
	public void pesquisarPorNome() {
		int min = 0;
		int max = 0;

		if (getScrollerPage() <= 0) {
			setScrollerPage(1);
			max = 10;
		} else {
			min = (getScrollerPage() * 10) - 10;
			max = getScrollerPage() * 10;
		}
		
		FuncionarioDao dao = new DaoFactory().getFuncionarioDao();
		List<Funcionario> funcionarios=new ArrayList<Funcionario>();
		
		try {
			funcionarios=dao.findAllByName(getNomePesquisado(), min, max);
		} catch (NoResultException e) {
			mostraMensagemAviso(" Nenhum funcionário encontrado.");
		}

		this.setCurrentState(PESQUISAR_STATE);
		this.setLista(funcionarios);
	}
	
	public void voltar() {
		limpar();
		preparePesquisar();
	}

	public void limpar(){
		super.limpar();
		funcionario=new Funcionario();
		nomePesquisado="";
	}

Desta forma quando o Formulario for submetido, o JSF instanciara o FuncionarioBean e invocara o setter correspondentes dos campos no form, onde meu POJO Funcionari estaria preenchido;

Então o que faço é chamar o Service do qual mantém o controle Transacional desta forma:

public class FuncionarioService {
	
	
	public void criarFuncionario(Funcionario funcionario)throws PersistenceException{
		FuncionarioDao dao = new DaoFactory().getFuncionarioDao();
		dao.getSession().beginTransaction();
		dao.save(funcionario);
		try {
			dao.getSession().getTransaction().commit();
		} catch (HibernateException e) {
			dao.getSession().getTransaction().rollback();
			throw new PersistenceException("Erro ao criar Funcionario",e);
		}
		
	}
	
	public void atualizarFuncionario(Funcionario funcionario){
		FuncionarioDao dao = new DaoFactory().getFuncionarioDao();
		dao.update(funcionario);
	}

A desvantagem maior que vejo eh que na interface Dao/Repository eu tive que definir o metodo getSession(), o que não seria responsabilidade nem do Repository nem do Dao. Mas foi a maneira que encontrei para resolver o problema.

Perguntas:

Nesta arquitetura quando quisesse por exemplo somente consultar o Repository/Dao poderia chamar direto do FuncionarioBean ou seja da Apresentacao, ou mesmo assim deveria passar pelo Service?

Existiria uma outra class responsavel pelas transacoes, como poderia remover o metodo getSession() do Dao/Repository?

Obrigado

Por exemplo, aqui, como vc garante que a segunda data é igual à primeira ?
Deveria ser

	public Funcionario() {
		this.dataPrimeiroAcesso=new Date();
		this.dataUltimoAcesso=dataPrimeiroAcesso;
		this.ativo=true;
	}

A primeira regra de programação é : Conheça as API que usa.
Se vc investigar sobre hibernate vai descobrir existe um padrão chamado Session-in-View que era usado antigamente, e que básicamente controla a transação por sessão do usuário. Hoje, existem tecnicas mais evoluidas como o uso de ThreadLocal. Procura por HibernateUtils para ver como é isso.

O controle de transação é da aplicação: isto significa que não é do dominio. Logo, não é dos serviços ou repositorios.
A transação é controlada pelo aplicação, então ela tem que ser inicializada/terminada no MBean (explicitamente) ou em um proxy do Serviço (implicitamente). Não antes. Não depois.

bom, além disso um melhor controle de exceções ajudaria. Especialmente o encapsulamento do hibernate abaixo do DAO. Ou seja, ninguem sabe que o hiberante existe excepto o DAO. Isto também é SoC. Cada objeto deve ter apenas uma responsabilidade. Se todos conhecer o hibernate, session, etc… todos sabem demais.

O serviço ficaria então apenas

public class FuncionarioService {
	
	
	public void criarFuncionario(Funcionario funcionario)throws PersistenceException{
		FuncionarioDao dao = new DaoFactory().getFuncionarioDao();
		dao.save(funcionario);
		
	}
	
	public void atualizarFuncionario(Funcionario funcionario){
		FuncionarioDao dao = new DaoFactory().getFuncionarioDao();
		dao.update(funcionario);
	}

e o controle de transação, por exemplo, passaria para o MBean

	public void atualizar(){
	
                Session session =  hibernateUtils.getSession();

               try {
                     session.beginTransaction();
		
		      funcionarioService.atualizarFuncionario(funcionario);
		       limpar();
		      preparePesquisar();	

			session.getTransaction().commit();
		} catch (HibernateException e) {
			session.getTransaction().rollback();
			throw new PersistenceException("Erro ao criar Funcionario",e); //ok
		}

	}