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