Modelo anémico, CRUDs y frameworks

816 views
Skip to first unread message

jmbeas

unread,
Sep 23, 2010, 7:30:43 PM9/23/10
to Artesanos de Software
Hola a todos,

Como prometí, voy a arrancar preguntando algo técnico. Por favor, si
no se sienten cómodos discutiendo estas cosas en esta lista, dado que
soy nuevo aquí, siéntanse libres de decirme que me vaya con el ruido a
otra parte. :-)

Pues bien, mi duda tiene que ver con que hace años que me enfrento a
aplicaciones basadas en un modelo de dominio anémico (ver
http://martinfowler.com/bliki/AnemicDomainModel.html). Una UI, un
modelo de datos y una miríada de objetos de transporte (DTOs) que
acabo pasando de un lado a otro y llamándoles objetos de dominio.
Quizás todo tenga que ver con el hecho de que estas aplicaciones
parten de que todo es un CRUD (altas, bajas, modificaciones y
listados) y que, claro, es fácil usar frameworks o herramientas que
prometen simplificar el desarrollo de este tipo de "pantallas". Ya sé,
no me miren mal, ya sé que el "data entry" es algo superado, pero sigo
encontrandome con aplicaciones así y con serias dificultades para
pensar en cómo pasar de un diseño así a uno basado en un modelo rico.

Mi pregunta viene a ser: ¿qué estrategia me recomendáis para migrar
progresivamente de un modelo de dominio anémico a otro rico?

Muchas gracias,
Jose Manuel Beas

http://agilismo.es
http://jmbeas.iexpertos.com
http://twitter.com/jmbeas

Rafael Gutiérrez

unread,
Sep 23, 2010, 7:54:38 PM9/23/10
to artesanos-...@googlegroups.com
Si no existen pruebas unitarias, creo que lo primero seria empezar a escribir esas pruebas unitarias que te van a ayudar a validar que algún cambio que realices no rompe la funcionalidad actual del sistema.

Luego creo que podrías empezar a realizar pequeños refactorings migrando aquella lógica de negocio que es mas "natural" que la realicen los propios objetos. Esto es difícil porque puede depender mucho del criterio del programador pero creo que como reglas se pueden tomar los refactorings mas basicos del propio libro de Martin Fowler.

Después vendría lo mas complejo y que es encontrar lógica de negocio que debería estar encapsulada en objetos y que esta regada en todo el proyecto en código.

Pero bueno creo que tus herramientas basicas aqui serian el uso de pruebas unitarias y refactorings

Saludos

Rafa

2010/9/23 jmbeas <jose....@gmail.com>

José Manuel Beas

unread,
Sep 23, 2010, 8:16:48 PM9/23/10
to artesanos-...@googlegroups.com
Gracias, Rafael.

El tener pruebas unitarias (e incluso de integración y funcionales) lo daba por hecho. De lo contrario no veo viable una refactorización sin romper lo que ya funciona y, es más, ni tan siquiera un entorno de trabajo saludable. :-)

Mi preocupación viene más por encontrar una estrategia que me permita escapar de la situación actual con una arquitectura multicapas donde cada pequeño cambio en una funcionalidad puede llevar cambios en decenas y decenas de tests y código de producción. Piensa que muchos de nuestros tests están acoplados a esos DTOs porque, si bien estos en sí mismos no tienen lógica, sí se usan para las fixtures de los tests, por ejemplo.

La verdadera lógica de negocio está repartida por clases en la capa de presentación (validaciones y transformaciones fundamentalmente), en fachadas y servicios en la capa de negocio y también en DAOs de la capa de persistencia. En algunos proyectos estas dependencias no aparecen tan evidentes pero existen. Por ejemplo, anotaciones JPA que ocultan que nuestras @Entity están mapeadas con la BD y por tanto hay una (cierta) dependencia con la tabla correspondiente.

Un saludo,
2010/9/24 Rafael Gutiérrez <abadon.g...@gmail.com>

Javier Acero Guerra

unread,
Sep 24, 2010, 6:01:04 AM9/24/10
to artesanos-...@googlegroups.com
Hola a todo el mundo.

Yo me encuentro en una situación muy parecida a la que comentas Jose Manuel. Lo de los modelos de dominio anémicos parece que es un mal endémico...

Sinceramente, yo no me he planteado refactorizar las aplicaciones que actualmente están así por una cuestión de tiempo. Si tuviera que hacerlo preferiría empezar de cero, puesto que no son aplicaciones excesivamente grandes y me supondría menos quebraderos de cabeza. Asumo que tú no estás en esta situación y, de plantearte algo, tiene que ser la refactorización.

Te cuento lo que yo intentaría hacer. Pero ya te digo que es todo teoría y no te puedo hablar de resultados :

Yo lo que intentaría hacer es ir "adelgazando" la capa de aplicación, donde tendrás todos esos servicios que implementan la lógica de negocio e irlos bajando poco a poco a las entidades que corresponda.

La mejor estrategia para eso es tener, en primer lugar, un test que pruebe la funcionalidad completa. Con ese test de guarda-espaldas yo añadiría, con TDD, la funcionalidad a la entidad, y nuevo comportamiento al servicio para que delegue el trabajo en la entidad en vez de hacerlo él. Con esto hecho "desenchufaría" el comportamiento inicial y "enchufaría" el nuevo y comprobaría que el test funcional sigue diciendo que todo va bien.

Respecto a las dependencias con las entidades en los tests, es complicado. Yo he aplicado el patrón Object Mother (lo leí en el libro de TDD de Manning) y limitas parcialmente las dependencias con las entidades al encapsular la creación de ejemplos para tests en una clase en concreto. También hay que tener en cuenta que en el momento en el que el modelo deje de ser anémico probablemente las entidades tengan mayor encapsulación y por ahí rompas otras tantas dependencias en los tests. Mucho más no se me ocurre...

No sé si  lo que propongo es buena idea o no, pero es lo que he ido pensando después de darle vueltas al asunto durante bastante tiempo.

Un saludo y perdón por la parrafada.

PD: Seguramente Michael Feathers y su Working With Legacy Code sean mucho mejor lectura que este correo. ;-)
--
Javier Acero
@jacegu

Alfredo Casado

unread,
Sep 24, 2010, 6:36:17 AM9/24/10
to artesanos-...@googlegroups.com
Justo ahora estoy releyendo el Domain Driven Design de Eric evans jeje, así que estoy muy sensible con el tema de los modelos :P (cuando leí este libro la primera vez me quede sólo con alguna idea superficial, después de varios años lo vuelves a leer y parece que estoy leyendo otro libro jeje, ahora entiendo muchas más cosas).

En primer lugar, si la aplicación es un CRUD es un CRUD, es decir, si no hay que hacer nada más que estas operaciones no hay mucho modelo de por medio que puedas meter, como máximo algunas validaciones o transformaciones como decia JMBeas. Quizás lo mejor en estos casos sea usar un framework tipo OpenXava para estas partes "aburridas" de la aplicación (nunca he usado este framework ni otros similares, pero de enfrentarme a una app de este tipo creo que merecerían un vistazo). Los modelos ricos sólo aparecen si hay lógica rica que modelar (de perogrullo ya lo se xd) así que tampoco hay que fustigarse por no conseguir un modelo rico, si la aplicación no lo necesita y el resultado es un código mantenible en mi opinión todo perfecto, como siempre no hay que olvidar que tener un modelo no es un objetivo es sólo un medio, y si la cosa es tan sencilla que no requiere más historia ¿cual es el problema?

Ahora otro problema es cuando la lógica si es rica pero se implementa fuera del modelo, en mi opinión el principal problema esta en lo que sea eso de la "capa de servicios", si lo que tienes es un montón de objetos de "modelo" que son solo dtos o representaciones 1-1 de las tablas y luego una serie de servicios "stateless" que actúan sobre esos objetos pues básicamente lo que tienes es un bonito programa estructurado. Un conjunto de objetos "procedimiento" sin estado que operan directamente sobre datos crudos (el supuesto modelo), esto no es orientación a objetos porque no estás modelando conceptos, estas modelando operaciones sobre datos.

¿Y si eliminamos la capa de servicios y implementamos la lógica lo más cerca de los datos que necesite para operar?, es decir, dentro de los objetos del modelo. Otra cosa que podemos probar es a implementar una aplicación sin usar getters&setters, a ver que nos sale.

Sobre las fixtures para test, os recomiendo el capitulo sobre esto del growing objects en mi opinión es un capitulo ESPECTACULAR, aboga por usar builders en lugar de object mother, nosotros estamos aplicando este patrón y nuestros test han mejorado enormemente.

Enrique Comba Riepenhausen

unread,
Sep 24, 2010, 2:44:46 AM9/24/10
to Artesanos de Software
Muy buenas a todos!

Soy nuevo en esta lista (gracias a JMB).

El problema de un dominio anemico es bastante comun, tristemente. Una
forma que yo he visto funciona muy bien para evitar un dominio anemico
es la siguiente (voy a suponer que es una aplicacion web por el
momento):

1) Mueve toda la logica a la vista: Basicamente lo que haces con esto
es que los controladores sean simplemente un router y los modelos un
contenedor de datos. Las vistas se convierten en algo tremendamente
horrible de ver lo que te fuerza a "ver" que logica deberia estar
donde.
2) Mueve lentamente toda la logica hacia tu modelos, pero no toques
los controladores. Haz eso hasta que los modelos sean amenos de usar
en la vista (i.e. hasta que el modelo sea el que sabe como conseguir
los datos, ser consistente, como cargar, etc.
3) Mueve la creacion del modelo (o la busqueda del modelo) al
controlador.

Si has hecho esto bien, tu controlador ahora solo tiene una (o dos)
lineas de codigo por metodo en las cuales llama al modelo y tus vistas
usan el modelo proporcionado por el controlador.

Es un poco dificil de explicar (o no me esoy explicando bien)...
quizas deberiamos hacer una session de pairing y te lo enseño.

Un abrazo,

Enrique

On 24 sep, 01:16, José Manuel Beas <jose.m.b...@gmail.com> wrote:
> Gracias, Rafael.
>
> El tener pruebas unitarias (e incluso de integración y funcionales) lo daba
> por hecho. De lo contrario no veo viable una refactorización sin romper lo
> que ya funciona y, es más, ni tan siquiera un entorno de trabajo saludable.
> :-)
>
> Mi preocupación viene más por encontrar una estrategia que me permita
> escapar de la situación actual con una arquitectura multicapas donde cada
> pequeño cambio en una funcionalidad puede llevar cambios en decenas y
> decenas de tests y código de producción. Piensa que muchos de nuestros tests
> están acoplados a esos DTOs porque, si bien estos en sí mismos no tienen
> lógica, sí se usan para las fixtures de los tests, por ejemplo.
>
> La verdadera lógica de negocio está repartida por clases en la capa de
> presentación (validaciones y transformaciones fundamentalmente), en fachadas
> y servicios en la capa de negocio y también en DAOs de la capa de
> persistencia. En algunos proyectos estas dependencias no aparecen tan
> evidentes pero existen. Por ejemplo, anotaciones JPA que ocultan que
> nuestras @Entity están mapeadas con la BD y por tanto hay una (cierta)
> dependencia con la tabla correspondiente.
>
> Un saludo,
> Jose Manuel Beas
>
> http://agilismo.eshttp://jmbeas.iexpertos.comhttp://twitter.com/jmbeas
>
> 2010/9/24 Rafael Gutiérrez <abadon.gutier...@gmail.com>
>
>
>
> > Si no existen pruebas unitarias, creo que lo primero seria empezar a
> > escribir esas pruebas unitarias que te van a ayudar a validar
> > que algún cambio que realices no rompe la funcionalidad actual del sistema.
>
> >  Luego creo que podrías empezar a realizar pequeños refactorings migrando
> > aquella lógica de negocio que es mas "natural" que la realicen los propios
> > objetos. Esto es difícil porque puede depender mucho del criterio del
> > programador pero creo que como reglas se pueden tomar los refactorings mas
> > basicos del propio libro de Martin Fowler.
>
> > Después vendría lo mas complejo y que es encontrar lógica de negocio
> > que debería estar encapsulada en objetos y que esta regada en todo el
> > proyecto en código.
>
> > Pero bueno creo que tus herramientas basicas aqui serian el uso de pruebas
> > unitarias y refactorings
>
> > Saludos
>
> > Rafa
>
> > 2010/9/23 jmbeas <jose.m.b...@gmail.com>

Javier Acero Guerra

unread,
Sep 24, 2010, 12:52:29 PM9/24/10
to artesanos-...@googlegroups.com
Hola otra vez.

Me apunto lo de el builder en vez del Object Mother. El libro de Growing Object Oriented Software lo tengo pero está en la pila de lecturas pendientes.

Respecto a lo de que las aplicaciones sean CRUD puro, tengo una opinión diferente: a mi entender, rara vez una aplicación es CRUD puro.  Nos hemos acostumbrado tanto a usar ORMs que nos cuesta ver la barrera entre lo que es lógica y lo que no lo es. Muchas veces disfrazamos el comportamiento del dominio como operación de base de datos, cuando en realidad es comportamiento que además implica operaciones en la base de datos al hacer persistente el estado. Pongo un ejemplo:

Típica situación de:    Alumno (1) -------------------------> (*) Asignatura

Cuando modelamos esta relación utilizamos una colección de Asignaturas en el Alumno. El hecho de que un alumno se matricule de una asignatura es una operación a nivel de base de datos simplemente: añadir a la colección de asignaturas del alumno una asignatura. Simplemente estamos añadiendo una relación a nivel de base de datos.

Esto, normalmente, lleva a una situación típica en el controlador (de al capa esa de servicios que comentábamos) del tipo: 

alumno.getAsignaturas().add(asignaturaDeLaQueSeMatricula)

Y aquí es donde realmente comienza el problema: 
  • Estás acoplando el controlador a la implementación interna del alumno, y a la representación de sus asignaturas.
  • Estás violando el principio tell, don't ask.
  • No estás respetando el patrón GRASP "Experto en información"
  • Estás violando la ley de Demeter.
  • ...
Yo añadiría un método al alumno que fuera matricularseDe y lo que eso implique para la clase alumno y a nivel de base de datos es cosa de esa clase. Ahora puede ser una relación con la tabla asignaturas mediante una colección, en el futuro puede cambiarse por otra cosa.

Igual tu lo estabas viendo desde otro punto de vista Alfredo y no te he entendido. Pero esta discusión ya la he mantenido con algún compañero de trabajo y me tiene bastante frito.

Me gustaría conocer la opinión que tenéis sobre esto.

Un saludo.

Alfredo Casado

unread,
Sep 24, 2010, 1:57:20 PM9/24/10
to artesanos-...@googlegroups.com
Me parece un buen ejemplo, yo lo haría igual, un método en alumno para añadir la asignatura esta mucho mejor, incluso el método "getAsignaturas" no debería existir en ningún caso. Yo me refería a casos extremadamente simples donde no hay siquiera relaciones "tengo una tabla y quiero ver lo que pone en una pagina web" vamos. Aunque a veces quizás también cabría preguntarse que sentido tienen este tipo de requisitos y si no se hacen las cosas así muchas veces por pura inercia.

Rafael Gutiérrez

unread,
Sep 24, 2010, 2:15:47 PM9/24/10
to artesanos-...@googlegroups.com
Si es un buen ejemplo.

Creo que parte del problema ha sido el "estandar" JavaBeans (que yo diría que es mas un hack) de tener getters y setters en nuestros Pojos para el caso de Java y que de repente nos obliga a tener algo como lo siguiente en el codigo:

public void setEstatus(Long idEstatus) {
    this.idEstatus = idEstatus;
}

public void activar() {
    this.setEstatus(1L);
}

public void desactivar() {
    this.setEstatus(0L);
}

Y cuando un usuario de este pojo ve estos tres métodos pues se le hace mucho mas facil usar el "setEstatus".

Saludos

Rafa Gutierrez

2010/9/24 Alfredo Casado <casado....@gmail.com>

Alejandro Cuesta

unread,
Sep 25, 2010, 6:29:07 AM9/25/10
to Artesanos de Software
Hola a todo el mundo,

Me encanta esta discusion, es el tema recursivo que tengo en la cabeza
desde que me dio por leer Domain Driven Design y asistir a alguna
charla de CQRS.

1) Yo no creo que el modelo anemico este tan mal. Hasta ahora ha
funcionado aplicandolo bien. El problema es que es mas dificil juntar
los conceptos bajo los mismos paquetes. Yo siempre he usado una capa
presentacion, servicios, dominio y dao. La logica de negocio siempre
la he puesto en servicios y dominio.

Problemas de este modelo:

- Los objetos "dominio" no lo son realmente, son DTOs y la verdadera
logica va en la capa "servicios".

- Cuesta mas trabajo dividir lo que es logica de negocio de lo que es
logica de la presentacion. Mucha gente termina metiendo logica de
presentacion en la capa servicios (ejemplo, decisiones para meter
informacion en HttpSession).

- Mas dificil de testear o "diseñar mediante ejemplos" que es como me
gusta llamarlo. Se hacen tests por clase en lugar de tests por
historia/escenario y se acaba abusando de los mocks, lo que incrementa
la dependencia test/implementacion.

De todas formas, sabiendo distinguir conceptos y aunque es mas
engorroso, funcionar, funciona.

2) No se exactamente a que os referis con eso de "CRUD puro". Creo que
en toda mi carrera siempre he visto applicaciones que dependen de
algun tipo de "repositorio" (base de datos, web service, solr...). Lo
que si creo que merece la pena distinguir es entre "lecturas" y
"escrituras" que es lo que sugiere CQRS.

En una aplicacion web normalmente hay muchisimas mas peticiones de
lectura que escritura. De ahi que mucha gente se incline a usar alguna
herramienta como Solr. Si divides la aplicacion en dos partes, lectura
y escritura, la parte de lectura es realmente sencilla de hacer.
Normalmente no suele haber logica y, si eres valiente, en casi todos
los casos podrias incluso llamar los DAOs desde la capa presentacion
(supongo que eso es lo que llamais CRUD impuro, o sea Read). La
escritura es mas problematica (y supongo que eso es lo que llamais
CRUD puro). Esta parte siempre tiene consecuencias sobre la lectura,
problemas de informacion obsoleta (cuando un usuario lee algo que
inmediatamente ha sido modificado), concurrencia, y es donde las
reglas de negocio suelen vivir.

Tengo la intencion de empezar a usar DDD en mi proximo proyecto
definitivamente. La idea es tener tres capas: Aplicacion, Dominio e
Infraestructura. Toda la logica va en Dominio y se desarrolla a partir
de "ejemplos" (tests). Infraestructura implementa las interfaces de
repositorio que el Dominio necesite. Y Aplicacion presenta la
informacion modelada en el Dominio.

Alex




On Sep 24, 7:15 pm, Rafael Gutiérrez <abadon.gutier...@gmail.com>
wrote:
> Si es un buen ejemplo.
>
> Creo que parte del problema ha sido el "estandar" JavaBeans (que
> yo diría que es mas un hack) de tener getters y setters en nuestros Pojos
> para el caso de Java y que de repente nos obliga a tener algo como lo
> siguiente en el codigo:
>
> public void setEstatus(Long idEstatus) {
>     this.idEstatus = idEstatus;
>
> }
>
> public void activar() {
>     this.setEstatus(1L);
>
> }
>
> public void desactivar() {
>     this.setEstatus(0L);
>
> }
>
> Y cuando un usuario de este pojo ve estos tres métodos pues se le hace mucho
> mas facil usar el "setEstatus".
>
> Saludos
>
> Rafa Gutierrez
>
> 2010/9/24 Alfredo Casado <casado.alfr...@gmail.com>
> >> *
> >> *
> >> *alumno.getAsignaturas().add(asignaturaDeLaQueSeMatricula)*
>
> >> Y aquí es donde realmente comienza el problema:
>
> >>    - Estás acoplando el controlador a la implementación interna del
> >>    alumno, y a la representación de sus asignaturas.
> >>    - Estás violando el principio *tell, don't ask.*
> >>    - No estás respetando el patrón GRASP* "Experto en información"*
> >>    - Estás violando la ley de Demeter.
> >>    - ...
>
> >> Yo añadiría un método al alumno que fuera *matricularseDe* y lo que eso

jcesarperez

unread,
Sep 29, 2010, 4:01:47 PM9/29/10
to Artesanos de Software
Hola a todos.

¿Soy el único al que le parece que meter en un objeto de dominio las
funciones de:

* Contener e inicializar los datos
* Mapeos objeto-relacional
* Reglas de validacion
* Reglas de transaccionalidad
* Llamadas a daos
* Lógica de negocio
* Métodos equals, hashCode y toString

es romper con la cohesión y el principio de Única responsabilidad?

Pej: en vuestro ejemplo del alumno que se quiere matricular de una
asignatura. ¿Quién valida si el alumno no está ya matriculado de esa
asignatura? ¿Si en la asignatura caben alumnos? ¿Si la asignatura
existe? ¿Si el alumno cumple los requisitos para matricularse en esa
asignatura? Etc.
¿Al final no terminariamos sacando esa funcionalidad a una clase
MatriculacionService para tener el código más limpio? O como queramos
llamarla para que no parezca un Service...

Un saludo.
Julio.
> ...
>
> leer más »

Javier Acero Guerra

unread,
Sep 29, 2010, 4:47:30 PM9/29/10
to artesanos-...@googlegroups.com
Hola otra vez.

Si se diera el caso de que fueran necesarias todos esos pasos está claro que probablemente habría que crear un servicio para efectuar la matriculación (ya que estamos con ese ejemplo). Y habría que ir encapsulando esas validaciones en otras clases, etc...

Pero aún en ese caso, si todo fuera correcto, el servicio debería acabar invocando el método matricularDe de la clase Alumno y no hacer el ya mencionado alumno.getAsignaturas().add(asignatura).

Este servicio sería un servicio propio del dominio conforme a la definición que da Eric Evans en Domain Driven Design.

Respecto a las funciones que no te gusta asignar al dominio, sinceramente no veo cómo lo quieres construir, sin lógica, ni datos, ni validaciones ni métodos equals o hashCode que, por poco que nos guste, son "imposiciones" del lenguaje (de uno en concreto).

¿Qué responsabilidades asignarías tú al dominio?
 
Un saludo.

Alfredo Casado

unread,
Sep 29, 2010, 7:39:00 PM9/29/10
to artesanos-...@googlegroups.com
¿Quién valida si el alumno no está ya matriculado de esa
asignatura? ¿Si en la asignatura caben alumnos? ¿Si la asignatura
existe? ¿Si el alumno cumple los requisitos para matricularse en esa
asignatura?

a ver que te parece esta solución:

class ReglasDeMatriculacion {
        public boolean satisfaceLasReglas(Alumno alumno, Asignatura asignatura) {
                   return alumno.noEstaMatriculadoEn(asignatura) && 
                            asignatura.tienePlazasDisponibles() &&
                            alumno.cumpleRequisitosParaMatricularseEn(asignatura); 
        }
}

class ServicioDeMatriculacion {
         public boolean matricularAlumno(Alumno alumno, Asignatura asignatura) {
                   if (!reglasMatriculacion.satisfaceLasReglas(alumno,asignatura)) 
                        return false;
                   
                   alumno.matricularDe(asignatura);
         }
}

La responsabilidad de las reglas es aplicar las reglas de matriculación que nos ha dicho el cliente (julio cesar jeje)
La responsabilidad del servicio es matricular al alumno si cumple las reglas (sean las que sean, abstracción).
La responsabilidad del alumno es matricularse de algo (que implicara escribir algo en la bd usando alguna clase de la capa de infraestructura) y saber si tiene los requisitos para matricularse (esto no lo has definido todavía, así que cuando se defina pensamos en los detalles jeje).

Así por lo menos tienes un dominio con comportamiento y que encapsula las reglas de tu negocio, la otra opción es que tu servicio sólo se hable con los distintos DAO's y vaya inspeccionando que tiene cada objeto tratándolo como una mera estructura de datos para aplicar todas estas cosas con unos cuantos "if's" encadenados.
 
Conste que a lo mejor no es un diseño muy bueno, es la primera aproximación al problema, los diseños se van refinando, pero por lo menos no tiene ningún getter ni ningún setter.

jcesarperez

unread,
Sep 30, 2010, 6:07:47 AM9/30/10
to Artesanos de Software
@JavierAceroGuerra: nosotros asignamos las responsabilidades de
contener, inicializar y validar los datos, más los mapeos objeto-
relacional (en caso de que usemos ORM), más los métodos equals,
hashCode y toString. Y dejo fuera todo el tema de daos, transacciones
y lógica de negocio.
No digo -ni creo- que sea lo mejor, por eso pregunto y quiero entender
la propuesta DDD. De hecho por eso me apunte a la lista, para
aprender! y me apunto el famoso libro de Evans para echarle un ojo. No
sabía que además de entidades también se incluyera la definición de
"servicios del modelo".
Entonces, ¿todas estas clases (entidades y servicios) las poneis en
vuestro paquete modelo juntas o dentro de modelo separais entidades de
servicios?

@Alfredo: jeje me está gustando sí.

Cosas buenas que le veo a simple vista. Separación de
responsabilidades y que ahora puedo asignar el control de
transacciones al método del servicio porque al final de ese método
iría el tipico dao.add, ¿no?

Lo de los requisitos de las asignaturas sería algo así como: debes
tener aprobadas antes ciertas asignaturas y no debes haberte
presentado a más de 5 convocatorias. Luego habría que invertir el
control, ya que el alumno a priori no debe ser el que conozca estas
reglas. Es decir: asignatura.cumpleRequisitosParaMatricularse(alumno).

Pero entonces, llega mi duda. Si para ejecutar las reglas hubiera que
realizar alguna nueva operación en bdd, ¿llamarías al dao o servicio
correspondiente desde la entidad Asignatura?

En general veo que escribimos código muy parecido. La gran diferencia
en este ejemplo sería que el metodo alumno.matricularDe yo lo habría
llamado alumno.insertarNuevaAsignatura y que yo tengo 2 capas
explicitas servicios y modelo.
Bueno y que yo tendría todos los get/set implementados por defecto.
Otra pregunta: eso de no tener get/set no os da problemas con
Hibernate u otras tecnologías (jsp, jsf, el, servicios web)?

Un saludo.
Julio.
> > invocando el método *matricularDe *de la clase *Alumno* y no hacer el ya
> > mencionado *alumno.getAsignaturas().add(asignatura)*.
>
> > Este servicio sería un servicio propio del dominio conforme a la definición
> > que da Eric Evans en *Domain Driven Design*.
>
> > Respecto a las funciones que no te gusta asignar al dominio, sinceramente
> > no veo cómo lo quieres construir, sin lógica, ni datos, ni validaciones ni
> > métodos equals o hashCode que, por poco que nos guste, son "imposiciones"
> > del lenguaje (de uno en concreto).
>
> > ¿Qué responsabilidades asignarías tú al dominio?
>
> > Un saludo.
>
> > El 29 de septiembre de 2010 22:01, jcesarperez <
> > julio.cesar.perez.arq...@gmail.com> escribió:
> ...
>
> leer más »

Alfredo Casado

unread,
Sep 30, 2010, 7:02:51 AM9/30/10
to artesanos-...@googlegroups.com
La gran diferencia en este ejemplo sería que el metodo alumno.matricularDe yo lo habría
llamado alumno.insertarNuevaAsignatura

Tu imagínate que eres un alumno que va andando por la calle, alguien viene y te dice:
    - "alumno ven aquí que voy a insertar una asignatura nueva" o bien
    - "alumno ven aquí que te voy a matricular de esta asignatura".

A mi la primera opción me da hasta miedo :P.

al método del servicio porque al final de ese método iría el tipico dao.add, ¿no?

No, eso ya lo hace el alumno al decirle que se matricule de algo, matricularse de algo en tu sistema significa ir a la base de datos y escribir un registro y reflejarlo en memoria, ¿porque sólo vas a resolver la mitad del problema y dejar la otra mitad al servicio?, usa el dao desde dentro del alumno. El dominio es un conjunto de clases para resolver tu problema y que las cosas se guarden en la base de datos es parte de tu problema, tus objetos de dominio no tienen porque ser ignorantes de esto y sólo trabajar en memoria. Que el Alumno tenga un colaborador que se encargue de ir la bd a escribir, y este colaborador es una clase de la capa de abajo (infraestructura).

Si para ejecutar las reglas hubiera que realizar alguna nueva operación en bdd, ¿llamarías al dao o servicio correspondiente desde la entidad Asignatura?

En general, si una clase para cubrir con las responsabilidades que le has definido necesita acceder a la base de datos o a donde sea para obtener información, pues que lo haga ¿porque no?, para hacerlo simplemente necesitas que esa clase tenga los colaboradores adecuados que sepan hacer esas cosas.

jcesarperez

unread,
Sep 30, 2010, 9:38:22 AM9/30/10
to Artesanos de Software
A mi que me inserten cualquier cosa me da miedo! Que uno ya tiene una
edad...

No pero en serio, el alumno no se matricula, en todo caso solicita
matricularse. Luego al alumno se le añaden asignaturas, pero soy
totalmente reacio a usar la Ñ, por eso lo de insertar... Ok, esto es
semántica, aunque creo que es relevante.

Respecto a lo de los colaboradores en las entidades. Si la entidad la
obtienes con Hibernate, ¿cómo le inyectas los colaboradores?

Gracias por tu tiempo Alfredo y un saludo a todos.
Julio.

On 30 sep, 13:02, Alfredo Casado <casado.alfr...@gmail.com> wrote:
> *La gran diferencia en este ejemplo sería que el metodo alumno.matricularDe
> yo lo habría
> llamado alumno.insertarNuevaAsignatura*
> *
> *
> *Tu imagínate que eres un alumno que va andando por la calle, alguien viene
> y te dice:*
> *    - "alumno ven aquí que voy a insertar una asignatura nueva" o bien*
> *    - "alumno ven aquí que te voy a matricular de esta asignatura".*
> *
> *
> *A mi la primera opción me da hasta miedo :P.*
> *
> *
> *al método del servicio porque al final de ese método iría el tipico
> dao.add, ¿no?*
> *
> *
> No, eso ya lo hace el alumno al decirle que se matricule de algo,
> matricularse de algo en tu sistema significa ir a la base de datos y
> escribir un registro y reflejarlo en memoria, ¿porque sólo vas a resolver la
> mitad del problema y dejar la otra mitad al servicio?, usa el dao desde
> dentro del alumno. El dominio es un conjunto de clases para resolver tu
> problema y que las cosas se guarden en la base de datos es parte de tu
> problema, tus objetos de dominio no tienen porque ser ignorantes de esto y
> sólo trabajar en memoria. Que el Alumno tenga un colaborador que se encargue
> de ir la bd a escribir, y este colaborador es una clase de la capa de abajo
> (infraestructura).
>
> *Si para ejecutar las reglas hubiera que realizar alguna nueva operación en
> bdd, ¿llamarías al dao o servicio correspondiente desde la entidad
> Asignatura?*
> *
> *
> En general, si una clase para cubrir con las responsabilidades que le has
> definido necesita acceder a la base de datos o a donde sea para obtener
> información, pues que lo haga ¿porque no?, para hacerlo simplemente
> necesitas que esa clase tenga los colaboradores adecuados que sepan hacer
> esas cosas.
> *
> *
> El 30 de septiembre de 2010 12:07, jcesarperez <
> julio.cesar.perez.arq...@gmail.com> escribió:
> ...
>
> leer más »

Alfredo Casado

unread,
Sep 30, 2010, 10:01:44 AM9/30/10
to artesanos-...@googlegroups.com
No pero en serio, el alumno no se matricula, en todo caso solicita
matricularse. Luego al alumno se le añaden asignaturas, pero soy
totalmente reacio a usar la Ñ, por eso lo de insertar... Ok, esto es
semántica, aunque creo que es relevante.

Las cuestiones semánticas son muy relevantes, para resolver el conflicto habría que preguntar al usuario, pregúntale ¿que haces con el alumno?:
    - matricularle de una asignatura.
    - añadirle una asignatura.

En mi opinión lo matricula, la clase alumno no representa a un alumno físico, representa la lógica de tu aplicación relacionada con el alumno, y en mi opinión que los alumnos se matriculen añade más información que sólo decir que se les añade una asignatura.

Si la entidad la obtienes con Hibernate, ¿cómo le inyectas los colaboradores?

Justo de esto hablaba con un compañero, que nadie pone colaboradores en las entidades porque las genera un framework, y ese framework sólo sabe leer de una bd y inyectar datos, valores de campos, pero no puedes meterle colaboradores y esto es una limitación impuesta por una arquitectura y puede que sea uno de los principales motivos por los que tenemos modelos anemicos.

Una solución es que los objetos del modelo los generes a través de un repository que internamente puede usar el framework que quieras, pero te da un punto donde puedes inyectar las dependencias que creas necesarias, por ejemplo con guice sería fácil:

class AlumnoRepository() {
       public getAlumnoByName(String name) {
          Alumno alumno = ORM.getAlumno(name) // aquí llamas al framework de ORM como sea según el que uses
          injector.injectMembers(alumno); 
      }
}

Lo único malo es que la inyección de esos constructores sería por setters o propiedades y no en el constructor que es la que más me gusta, pero menos da una piedra. Quizás se podría buscar una solución más elegante con AOP por ejemplo, de modo que interceptas las llamadas a los métodos del orm que generén objetos y les inyectas cosas despues, no se, habría que pensarlo más.

Nosotros no usamos ningún ORM, por eso no tenemos este problema. Pero que los ORM te impidan poner colaboradores en las entidades me parece un problema muy serio, ¿como se puede construir un modelo rico sin colaboradores?

Alfredo Casado

unread,
Sep 30, 2010, 10:07:51 AM9/30/10
to artesanos-...@googlegroups.com
Googleando un poco sobre el tema: http://blog.igorstoyanov.com/2005/12/dependency-injection-or-service.html

no somos los primeros que discutimos sobre el tema jeje.

Alejandro Cuesta

unread,
Sep 30, 2010, 5:23:09 PM9/30/10
to Artesanos de Software


On Sep 29, 9:01 pm, jcesarperez <julio.cesar.perez.arq...@gmail.com>
wrote:
> Hola a todos.
>
> ¿Soy el único al que le parece que meter en un objeto de dominio las
> funciones de:
>
> * Contener e inicializar los datos

Yo suelo iniciar los objectos de forma que no de lugar a
inconsistencias. Segun DDD se pueden meter en el mismo objeto o en una
factoria.

> * Mapeos objeto-relacional

Esto se divide en dos: en el dominio se declara y se usa la interfaz.
En la capa de infraestructura se implementa la interfaz
O sea, que si usar llamas a un DAO o repositorio, dominio pero si
hacer una implementacion en hibernate o jdbc lo metes en la capa de
infraestructura.

> * Reglas de validacion

Esto se llama Espeficifacion en DDD y siempre es bueno mantener las
reglas en un solo lugar de forma que el resto de objectos preguntan a
la espeficicacion si cierta regla se cumple.

> * Reglas de transaccionalidad
> * Llamadas a daos
Creo que estas ya esta contestado en la de mapeos

> * Lógica de negocio
En el dominio.

> * Métodos equals, hashCode y toString

Ein? Esto es propio del lenguaje. De todas formas, donde vas a
implementar esto si no es en el mismo objeto. Recuerda, siempre que se
implementa equals, hay que implementar hashcode.

>
> es romper con la cohesión y el principio de Única responsabilidad?
La mejor forma de detectar cuando se rompe el principio de Unica
Responsabilidad es mediante unit tests, en mi opinion.
Cuando un test se complica, se hace dificil de leer y se alarga es un
claro sintoma de que el objeto cuyo comportamiento estas definiendo
esta haciendo demasiadas cosas. Si hace solo una, es facil de testear.
Si hace mas de una es mas dificil.

Reply all
Reply to author
Forward
0 new messages