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.
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