Atencao: A mensagem que se segue ficou um tanto grande, e nao cobre todos os pontos que gostaria. Portanto, ao inves de fazer um livro com as minhas duvidas, vou faze-las em partes, de acordo com o andamento do topico.
Atualmente, a logica de negocios do JForum esta basicamente nas actions - como “logica de negocios” aqui, por favor entendam coisas como verificacao de seguranca, validacao de parametros, definicao do contexto do freemarker e acesso aos DAOs. Ha bastante codigo em classes utilitarias tambem, as quais foram movidas para tais pois eram usadas em mais de um lugar. Nao vou entrar em detalhes de implementacao por nao haver mta necessidade.
Para sair dessa meleca, comecei a refatorar o JForum. A cada hora que passa vejo cada vez mais que estou a ponto de reescrever uma enorme parte do codigo fonte. Embora o codigo atual esteja limpo e, principalmente, facil de entender e manter, falta pouco para um limite da arquitetura, onde entao ficara muito dificil adicionar coisas novas sem prejudicar a estabilidade do sistema (aka, falta de testes da nisso).
Conversando com o Villela e dando uma lida no PoEAA, GOF e Core J2EE Patterns (fora material da net), acabei ficando com muitas duvidas em relacao a como fazer o refactoring. De uma maneira ampla, alem dos ja citados elementos “extensiblidade” e “testabilidade”, faz-se necessario ter uma API de servicos, afim que seja possivel interagir com o JForum usando meios outros que somente um browser.
Tendo isso em mente, o que tenho (praticamente) definido eh assim:
:arrow: As actions somente irao interagir com os servicos, e fazer coisas especificas ao ambiente - no caso de web / browser normal, eh configurar o contexto do sistema de templates / criar objetos a serem passados aos servicos.
:arrow: Sobre “servico” eu entendo como um conjunto de metodos que servem como ponto de entrada / delegadores para a camada de negocios.
Ai comecam a pintar as maiores duvidas, sendo um tanto dificil achar um ponto de partida. Vou explicar o que comecei a fazer, tentar dar alguma justificativa, e esperar que alguem aqui tenha uma boa ideia. A primeira ideia que eu tive foi, obviamente, tirar todo codigo “de negocios” das actions, e mover para classes de servico, como PostService, TopicService e etc… tais classes verificam se o usuario tem direito de execucao na tarefa, se o topico solicitado existe, e entao chamam os metodos dos DAOs para fazer a persistencia. Simplificando o codigo, fica algo como
// PostService.java#delete(int postId)
if (!SecurityRepository.canAccess(SecurityConstants.PERM_MODERATION_POST_REMOVE)) {
throw new ServiceException(ServiceErrors.POST_CANNOT_DELETE);
}
// ...
PostDAO pdao = DataAccessDriver.getInstance().newPostDAO();
Post p = pdao.selectById(postId);
// ...
pdao.delete(p);
// ...
DataAccessDriver.getInstance().newUserDAO().decrementPosts(p.getUserId());
tdao.decrementTotalReplies(this.underlyingPost.getTopicId());
int minPostId = tdao.getMinPostId(p.getTopicId());
if (minPostId > -1) {
tdao.setFirstPostId(p.getTopicId(), minPostId);
}
// ...
fdao.setLastPost(p.getForumId(), maxPostId);
Ou seja, o servico se encarrega de cuidar das dependencias e tudo mais. Porem, esse codigo nao esta me agradando muito, pois ainda parece um codigo bastante tanto acoplhado e especialmente fragil. O cv sugeriu usar listeners e mover boa parte da logica para os pojos, formando assim um domain model (ou algo parecido com isso). Indo por esse lado, o codigo anterior resultaria nisso:
Define o listener
interface PostListener {
void postDeleted(Post p);
}
Classes que precisam ser notificadas sobre alteracoes em posts implementam-a
interface UserDAO extends PostListener { ... }
class ConcreteUserDAO implements UserDAO {
void postDeleted(Post p) {
this.decrementPosts(p.getUserId();
}
void decrementPosts(int userId) { .... }
// ...
}
O mesmo para o topico
interface TopicDAO extends PostListener { ... }
class ConcreteTopicDAO implements TopicDAO {
void postDeleted(Post p) {
int totalPosts = this.getTotalPosts(p.getTopicId());
if (totalPosts < 1) {
this.delete(p.getTopicId());
return;
}
int maxPostId = this.getMaxPostId(p.getTopicId());
this.setMaxPostId(maxPostId);
int minPostId = this.getMinPostId(p.getTopicId);
this.setMinPostId(minPostId);
}
}
A classe de post se encarrega de notificar todo mundo
class Post {
static List listeners = // ...
void delete() {
PostDAO dao = // ...
dao.delete(this);
this.notifyPostDeleted();
}
void notifyPostDeleted() {
for (PostListeler pl : listeners) {
pl.postDeleted(this);
}
}
Os observers sao registrados no startup do sistema
Post.addListener(new ConcreteUserDAO());
Post.addListener(new ConcreteTopicDAO());
Removendo uma mensagem.
Post p = // ...
p.delete();
A BEM grosso modo eh isso. Nao tenho uma implementacao concreta, mas tenho varias duvidas. Dicas gerais sobre formas de estruturar o codigo, responsabilidades e tudo mais sao muito bem vindas.
Rafael