jueves, 4 de julio de 2013

Arquitecturas para Domain-Driven Design - Parte VII

Diseño final

Bueno final final... queda muchísimo por hacer y definir para tener una arquitectura de verdad, pero sí que puedo crear un esqueleto muy básico que puede dar una idea a grandes rasgos.

Una vez definida la colaboración entre las distintas capas del sistema, voy a dejar fragmentos de código de ejemplo para que el lector se haga una idea del resultado final.

En estos ejemplos se utiliza un contenedor de inyección de dependencias para resolver los servicios de aplicación que se van a utilizar, esto nos ayuda a tener un ServiceLocator que además se encarga de montar toda la cascada de dependencias entre clases. Cosa muy buena para tener una arquitectura débilmente acoplada. 



Vista

Partiendo de la premisa de que no tenemos un framework de vistas con sus controladores, presentadores, modelos de vista etc, que se integre con el contenedor de inyección de dependencias (como puede hacer JSF y SPRING o ASP.MVC 3 y Unity) el primer paso para realizar una acción notificada por el usuario, una vez que se ha recopilado con las vistas toda la información necesaria, es utilizar el localizador de servicios para resolver la instancia de la capa de aplicación que se encargue de esto. Una vez instanciada, se llama al método que hayamos creado para esta acción del usuario pasándole la información que ha recopilado la vista.

Public Class GestorClasificacionesPresenter

Public Sub SwapDescriptions
    Try
        'utilizamos el localizador de servicios para resolver la instancia de gestor de clasificaciones y que
       'monte todo el arbol de dependencias con el contenedor de inyeccion
        manager As IClasificationManagement = ServiceLocator.Current.GetInstance(Of        IClasificationManagement)()'clase de la capa de aplicacion que deseamos llamar
        manager.SwapDescrition(Vista.GetIDClasificacionSeleccionadaOrigen, Vista.GetIDClasificacionSeleccionadaDestino) 'un par de listas desplegables con las clasificaciones en la vista
    Catch ex As HandledException
        View.NotifyErrorToUser(ex)'se es html se mostrara un div modal, si es app de escritorio se mostrara un dialogo de error, etc...
    Catch ex As UnHandledException
        'algo inesperado ha pasado. Realizar las acciones pertinentes. (Depede del dominio, la tecnologia usada, el tipo de vista del cliente, etc)
    End Try

    View.NotifyOkToUser()

End Sub

End Class

Aplicación

Tenemos los identificadores de las Clasificaciones a las cuales les queremos intercambiar las descripciones obtenidas de la vista. Vamos a usar un servicio de Aplicacion de seguridad para comprobar la autorización del usuario para esta acción, utilizar la capa de persistencia para obtener las entidades Clasificación de origen y destino por su ID, utilizar un servicio de dominio para el negocio del intercambio de descripciones y a validar la integridad de las entidades después de la operación para comprobar que son válidas. En caso de ser necesario, se abrirían y cerrarían transacciones en este método puesto que es el que coordina la persistencia con el negocio.

Public Class ClasificationManagement
 Implements IClasificationManagement

        Private _seguridad As ISecurityService 'autorizacion
 Private _servicio As IClasificacionesService 'negocio de Clasificaciones
 Private _repositorio As IClasificationRepository 'persitencia de Clasificaciones
 Private _validator As IDataValidator 'validacion de Clasificaciones
        Private _UoW As IUnitOfWork 'unidad de trabajo para la persistencia

'el contenedor de dependencias inyecta todo lo que necesitamos en esta clase

 Public Sub New(ByVal servicio As IClasificacionesService, ByVal repositorio As IClasificationRepository, ByVal seguridad As ISecurityService, ByVal validator As IDataValidator, UoW as IUnitOfWork)
  _servicio = servicio
  _repositorio = repositorio
  _seguridad = seguridad
  _validator = validator
 End Sub

 Public Sub SwapDescrition(ByVal clasificationOrigenID As Integer, ByVal clasificationDestinoID As Integer) Implements IClasificationManagement.SwapDescrition

'seguridad
 If Not _seguridad.hasPermissions(Rol.AdvancedOperator Or Rol.BasicOperator) Then
   Throw New SecurityException(String.Format("No permissions for user: {0} whit roles: {1} to Swap Clasification Descirptions. This operation requires user has {2} or {3} role", _securityModule.NombreUsuario, _securityModule.ListsRolesPrettyPrint, Rol.AdvancedOperator.ToString(), Rol.BasicOperator.ToString))
 End If
  Dim txSettings As TransactionOptions = New TransactionOptions With {.Timeout = TransactionManager.DefaultTimeout, .IsolationLevel = IsolationLevel.ReadCommitted}

  Dim listaErrores As New System.Collections.Generic.List(Of String)
'abrimos una transaccion nueva
  Using scope As New TransactionScope(TransactionScopeOption.Required, txSettings)
'obtenemos de persistencia la clasificacion destino
   Dim clasificacionDestino As Clasificacion = _repositorio.GetClasificacionByID(clasificationDestinoID)
'obtenemos de persistencia la clasificaion origen
   Dim clasificacionOrigen As Clasificacion = _repositorio.GetClasificacionByID(clasificationOrigenID)
'capa de servicios de dominio para el negocio
   _servicio.swapDescription(clasificacionOrigen, clasificacionDestino)
'validamos la integridad de las entidades despues de modificarlas
   listaErrores.AddRange(_validator.Validate(clasificacionDestino))
   listaErrores.AddRange(_validator.Validate(clasificacionOrigen))
   If listaErrores.Any() Then
    Throw New ValidationException(listaErrores)
   End If
'todo bien, guardamos las modificaciones en persistencia unitlizando un patron unidad de trabajo
   _UoW.SaveChanges()
   scope.Complete()
  End Using 'si algo fallo, la transaccion se deshace gracias a la clausula Using

 End Sub

End Class

Dejándome en el tintero el resto de las capas del sistema para otro post, me queda comentar, para los puristas criticones, que hay varias maneras de mejorar esto utilizando programación orientada a aspectos o montando una cadena de responsabilidades de forma transparente aprovechando la inyección de dependencias. Pero esto es otra historia...

No hay comentarios:

Publicar un comentario