Autor: Jim Booth
Traducción de: Sergio E. Aguirre
El modelo N-Tier
El término N-tier se refiere a los varios niveles de responsabilidad en el diseño de un sistema. La N en N-Tier puede ser cualquier número de 2 en adelante. Un diseño muy común es el modelo 3-Tier. En el modelo 3-tier la aplicación es dividida en 3 capas de responsabilidades distintas, la interface del usuario, la lógica de negocio, y la base de datos. Cada una de estas capas puede ser implementada usando uno o más objetos que se dedican a las responsabilidades de esa capa.
Interface del usuario
La capa interface del usuario contendría todos los aspectos visuales del sistema. Cualquier cosa involucrada en la interacción con el usuario del sistema es manejada por esta capa. Todos los diálogos, cuadros de mensajes, formularios, informes, y otros componentes de interacción con el usuario residirían en la capa interface de usuario de un sistema.
Lógica de Negocio
La capa lógica de negocio tiene la responsabilidad de determinar la forma en que vienen los datos y cómo deben estructurarse para la interface del usuario. También aplica toda regla de validación a los datos provenientes de la interface del usuario antes de mandar estos datos a la base de datos.
La capa lógica de negocio no tiene ningún componente de la interface del usuario en ella como tampoco tiene la responsabilidad de actuar recíprocamente con el usuario. Los problemas percibidos con los datos deben ser comunicados a la capa interface del usuario a través de valores devueltos por métodos y la capa interface del usuario debe mostrar los mensajes al usuario.
Manejo de la Base de Datos
La base de datos es la responsable de manejar el dominio de validación sobre los datos, de actualizar y recuperar los datos en las tablas. Las reglas en la base de datos deben restringirse a sólo aquellas que son de directa aplicación del dominio de validación. "Reglas de Negocio" no son parte de las reglas de la base de datos, en cambio ellas fueron puestas en vigor en la capa lógica de negocio.
Otras Capas
3-Tier no es el único diseño N-Tier. N puede ser cualquier número. Algunas de las cosas que podrían ser consideradas para las capas adicionales son, Interface del Sistema Operativo, Interface de la Red, y los Múltiples Niveles de Capas de Lógica de Negocio.
Por ejemplo, usted puede diseñar un sistema para un
banco donde el objeto de lógica de negocio para una Cuenta necesita
tener varios formatos diferentes dependiendo de la sección del banco
que está usando los datos. En este caso usted puede tener un objeto de
lógica de negocio para Cuenta que es genérico para el banco
entero, y tiene otros objetos de lógica de negocio que son específicos
para las secciones particulares (usando el objeto Cuenta genérico y agregando
o restringiendo rasgos basados en los requisitos de la sección).
Ventajas / Desventajas del Diseño N-Tier
Las ventajas del diseño de un sistema N-Tier
son múltiples. La siguiente lista muestra algunas de las ventajas.
- Usted puede modificar la lógica de negocio sin hacer cambios a la
interface del usuario o a la base de datos.
- Si lo construyó correctamente, el objeto de lógica de negocio
puede ser usado por múltiples interfaces del usuario.
- Aisla el conocimiento requerido en cualquier capa dada a esa capa.
Algunas de las desventajas son:
- El diseño del sistema es más complejo.
- El enlace de datos inherente de Visual FoxPro es indisponible.
- La huella de memoria de la aplicación aumenta.
Con estas desventajas, ¿Por qué querría alguien
construir un sistema N-Tier? La respuesta es una sola palabra, scalability.
El diseño N-Tier puede descascarar a los sistemas sumamente grandes sin
compromisos. Por grande estamos refiriéndonos al número de usuarios,
el número de diferentes componentes de la interface del usuario,
el tamaño de la base de datos, la estructura de la red, y todos los otros
problemas de tamaño para una aplicación.
Usando el diseño N-Tier, usted puede diseñar
un sistema que pueda manejar múltiples intefaces del usuario divergentes
sin tener que volver a escribir la lógica de negocio para cada
interface construida. La lógica de negocio puede ser compartida
por múltiples interfaces del usuario. Mediante la posibilidad de hacer
subclases, las clases de lógica de negocio pueden personalizarse
para manejar diferentes servidores de base de datos.
El diseño N-Tier no es apropiado para todos los
proyectos, pero cuando se necesita de él es un concepto de diseño
sumamente poderoso.
Construyendo Aplicaciones N-Tier en Visual FoxPro
Visual Foxpro puede ser usado para construir cualquiera de
las capas comúnes del modelo N-Tier. Visual FoxPro tiene las herramientas
para construir interfaces del usuario destacadas. La base de datos nativa de
Visual FoxPro es rápida y robusta. Sin embargo, para cada una de estas
dos capas hay otras herramientas que también lo hacen bien o mejor. ¿Si
la interface necesita imitar a una hoja de cálculo, Excel no sería
una mejor opción? Si la base de datos necesita seguridad agregada en
la base de datos del servidor, no serían una opción mejor SQL
Server o Oracle?
La capa en la que Visual FoxPro tiene ventajas es la capa media,
o capa de lógica de negocio, del modelo N-Tier. Debido
a la construcción en Lenguaje de Manejo de Datos (DML) de Visual
FoxPro, es el primer candidato para manipular datos de un servidor y presentarlos
a una interface. También, la posibilidad de crear clases OLE públicas
con Visual FoxPro permite interfaces divergentes y bases de datos para usar
el mismo objeto de media capa para comunicarse entre sí.
Las Responsabilidades de un Objeto de Lógica de Negocio
Las responsabilidades de un objeto de media capa varían
ampliamente. Cosas como la aplicación de la regla de negocio, separación
de la interface del usuario y el origen de la base de datos, y proporcionando
una sola capa para el acceso a datos para los múltiples servidores de
la base de datos está entre las posibles responsabilidades.
Como con muchas otras cosas en el desarrollo orientado a objetos,
el diseño del sistema dicta las reales funciones proporcionadas por un
objeto de media capa.
Aplicando las reglas de negocio
En cualquier sistema de base de datos hay reglas que controlan
que son datos válidos y que son datos inválidos. Estas reglas
pueden ser divididas en el dominio de validación y reglas de negocio.
Un dominio describe todos los valores posibles que pueden encontrarse en la
entidad o atributo al que el dominio aplica. Por ejemplo, el dominio para un
campo ciudad puede incluir todos los nombres posibles de ciudades del
mundo.
Las reglas de negocio son un subconjunto de un dominio. Las
reglas de negocio llevan más allá el límite de valores
posibles para ser sólo parte del dominio completo. Con el ejemplo del
campo ciudad, quizás nuestra compañía se localiza
en Alemania y tiene clientes sólo alemanes. En este caso el dominio para
el campo ciudad será todas las ciudades del mundo, pero las reglas
de negocio limitarían al campo a sólo ciudades en Alemania.
Mientras que las bases de datos son muy buenas en la aplicación
de dominios, ellas pueden ser demasiadas restrictivas si ponen en vigor reglas
de negocio. El objeto de lógica de negocio de media capa es un
candidato ideal para poner en vigor las reglas de negocio. Visual FoxPro está
especialmente preparado para este trabajo porque su motor local de datos permite
la creación de diseños de metadata para describir las reglas
específicas a ser puestas en vigor. Esto le permite al programador crear
reglas de negocios para el manejo de datos que puedan cambiar con el tiempo
sin requerir cualquier modificación de código.
Trasladando Datos para la Interface del Usuario
En el diseño 3-Tier la interface del usuario
está separada del origen de datos por la capa media, o la capa de la
lógica de negocio. Esta separación le permite al programador construir
una interface del usuario independiente y capas de almacenamiento de datos.
Los perfeccionamientos futuros del sistema pueden incorporar nuevas interfaces
del usuario o nuevas tecnologías de almacenamiento de datos sin un cambio
que provoque la necesidad de cambiar el otro.
El objeto de media capa realiza el papel de traductor de datos
en el formato encontrado en el sistema de almacenamiento de datos a un formato
que puede ser usado por la interface del usuario. También traduce los
datos desde la interface del usuario a un formato que puede ser guardado por
la base de datos.
Con este diseño un cambio en la base de datos requiere
sólo que el objeto de media capa sea reforzado, así como un cambio
en la interface del usuario también sólo requiere que el objeto
de media capa sea reforzado.
Usando Clases de Visual FoxPro para construir un Objeto de
Lógica de Negocio
Los ejemplos se pueden descargar
aquí.
Exploremos ahora en algo de código para ver una de
las muchas maneras que usted puede diseñar un objeto de media capa en
Visual FoxPro. La clase de lógica de negocio que nosotros crearemos se
va a llamar customer y proporcionará acceso a los datos de ejemplo
Fitch Mather que vienen con Visual FoxPro 6.0. La tabla que usaremos
es la tabla Stores (Tiendas).
El primer problema con el cual yo traté era qué
clase de base usar para crear el objeto customer. Yo escogí usar
la clase de base form porque proporciona una sesión de datos privada
que protegerá los datos de otras instancias del objeto customer.
A la clase customer yo agregué una propiedad
llamada oRDS para usarse como una referencia a un Control de Datos RDS. Yo usé
RDS como la metodología de acceso a datos para que la clase customer
pueda ser fácilmente modificada para acceder a otros sistemas de base
de datos. El control de Datos RDS se crea en el Init de la clase Customer.
El código del método Init se describe debajo.
* Creo el Control de Datos RDS
This.oRDS = CreateObject("rds.datacontrol")
* Verificamos si la creación tuvo éxito
If Type("This.oRDS") <> "O"
* Si es no devuelvo .F.
Return .F.
Else
* Si tuvo éxito fijo algunas propiedades del datacontrol
With This.oRDS
* Establezco el nombre del origen de Datos
.Connect = "dsn=dsnFitchMather"
* Prepare la declaración SQL para ejecutarla
.SQL = "Select * from stores"
* Fijo la ejecución síncrona
.ExecuteOptions = adcExecSync
* Fijo el saque en el fondo
.FetchOptions = adcFetchBackground
* Ejecuto la consulta
.Refresh
EndWith
EndIf
Los comentarios en el código anterior son autoexplicativos. Una vez que el objeto customer existe tiene el control de Datos RDS dentro de él y el Control de Datos RDS está sacando los datos.
NOTA:
Las constantes referidas en el código anterior se toman de un archivo llamado adcvbs.h. Los contenidos de este se listan debajo.
*--------------------------------------------------------------------
* Microsoft ADC
* (c) 1997 Microsoft Corporation. All Rights Reserved.
* ADO constants include file for VBScript
*--------------------------------------------------------------------
*---- enum Values ----
#Define adcExecSync 1
#Define adcExecAsync 2
*---- enum Values ----
#Define adcFetchUpFront 1
#Define adcFetchBackground 2
#Define adcFetchAsync 3
*---- enum Values ----
#Define adcReadyStateLoaded 2
#Define adcReadyStateInteractive 3
#Define adcReadyStateComplete 4
A esta clase yo he agregado varios métodos que se listan en la siguiente tabla.
Método |
Propósito |
GetValue |
Usado para obtener el valor de un campo |
SetValue |
Usado para establecer el valor de un campo |
MoveFirst |
Mueve al primer registro en el RecordSet |
MoveLast |
Mueve al último registro |
MoveNext |
Mueve al próximo registro |
MovePrev |
Mueve al registro anterior |
Requery |
Refresca el Control de Datos mediante la re-ejecución de la consulta SQL. El método requery está para hacer que la sintaxis del objeto customer sea similar a la sintaxis nativa de VFP nativa para el requerying de una vista. |
RevertChanges |
Descarta los cambios pendientes de los datos |
SaveChanges |
Graba los cambios pendientes de los datos |
Las siguientes secciones presentarán el código que está en estos métodos.
GetValue
LPARAMETERS PcField
* Verifico por un parámetro válido
IF NOT EMPTY(pcField) AND VARTYPE(pcField) = "C"
* Verifico si es un nombre de campo válido para este objeto
IF LOWER(pcField) $ "store_name~store_add1~store_addr2~store_addr3~" + ;
"store_city~store_id~store_desc~store_phone1~" + ;
"store_state~store_type~store_zip"
* Demanda de un campo válido para este objeto, así que devuelvo el valor
RETURN THIS.oRDS.Recordset.Fields(pcField).Value
ENDIF
ENDIF
* Demanda de un campo inválida
RETURN .NULL.
SetValue
LPARAMETERS PcField, pxValue
IF NOT EMPTY(pcField) AND VARTYPE (pcField) = "C"
IF LOWER(pcField) $ "store_name~store_add1~store_addr2~store_addr3~" + ;
"store_city~store_id~store_desc~store_phone1~" + ;
"store_state~store_type~store_zip"
IF VarType(pxValue) = "C"
pxValue = ALLTRIM(pxValue)
ENDIF
THIS.oRDS.Recordset.Fields(pcField).Value = pxValue
RETURN .T.
ENDIF
ENDIF
RETURN .F.
MoveFirst
ThisForm.oRDS.RecordSet.MoveFirst
RETURN 1
MoveLast
ThisForm.oRDS.RecordSet.MoveLast
RETURN 1
MoveNext
LOCAL LnRet
lnRet = 1
ThisForm.oRDS.RecordSet.MoveNext
If ThisForm.oRDS.RecordSet.Eof
lnRet = -1
ThisForm.oRDS.RecordSet.MoveLast
EndIf
RETURN lnRet
MovePrev
LOCAL LnRet
lnRet = 1
ThisForm.oRDS.RecordSet.MovePrevious
If ThisForm.oRDS.RecordSet.Bof
lnRet = -1
ThisForm.oRDS.RecordSet.MoveFirst
EndIf
RETURN lnRet
Requery
ThisForm.oRDS.Refresh
RevertChanges
THISFORM.Requery()
SaveChanges
ThisForm.oRDS.SubmitChanges()
THIS.Requery()
Usted puede preguntarse por qué yo he creado todos estos métodos para hacer cosas que podría haber hecho refiriéndome directamente al Control de Datos RDS. La respuesta es que esto proporciona una interface de desarrollo para el objeto de negocio que es independiente de la naturaleza del objeto de datos. Yo puedo hacer subclases de esta clase y escribir código que con datos locales de VFP, o ADO en lugar de RDS, o ODBC a través de vistas remotas. Ninguno de éstas modificaciones requeriría que cualquier código, en el nivel de UI, sea cambiado en absoluto.
Usando el Objeto de Negocio
El proyecto también incluye un formulario de VFP que demanda esta clase de negocio. El nombre del formulario es Customer.scx. Examinemos el código de este formulario que usa la clase de negocio.
En este formulario se agrega una propiedad llamada oBusObj. En el evento Load del formulario el código será el siguiente.
THISFORM.oBusObj = NewObject("Customer")
Esto crea una instancia del objeto customer y almacena una referencia a él en la propiedad oBusObj del formulario. En el Refresh del textbox número de tienda este es el código:
THIS.Value = THISFORM.oBusObj.GetValue("store_id")
Este código llama al GetValue del objeto de negocio y fija la propiedad Value del textbox para ser el valor devuelto por el método. El evento Valid para el mismo textbox es:
THISFORM.oBusObj.SetValue("store_id",THIS.Value)
Que escribe el Valor devuelto a la fuente de datos del objeto de negocio.
El evento Click para el botón Top es:
THISFORM.oBusObj.MoveFirst()
THISFORM.Refresh()
¿Está empezando a ver un patrón aquí? ¿Puede apreciar lo fácil que es usar este objeto de negocio para manejar el acceso de datos? El otro código en el formulario es similar salvo que se llaman métodos del objeto de negocio.
Creando un servidor ActiveX desde las clases de Visual FoxPro
Así que, ¿Cuál es el gran trato? ¿Por qué es mejor usar un objeto separado para manejar el acceso a datos cuándo el formulario tiene que un entorno de datos maravilloso y controles que pueden enlazar directamente los datos?
La respuesta a estas mentiras es la palabra scalability. Scalability es la habilidad de un sistema de crecer incluir nuevos rasgos con el tiempo, grandes volúmenes de datos, interfaces del usuario adicionales, y otros perfeccionamientos. Si usted demanda el entorno de datos de un formulario de VFP por acceder a los datos, entonces usted se limita a usar VFP para construir la interface del usuario o usted necesitará crear las mismas capacidades de acceso a datos en alguna otra herramienta.
¿Qué pasa si las mismas necesidades de acceso a datos estén disponibles a VFP y a Microsoft Excel? La respuesta es hacer a la clase customer como una clase OLE Pública y construir un COM DLL con ella incluida. Para hacer una clase OLE Pública, abra el diseñador de clases y luego elija Información de clase... en el menú Clase. Verifique que esté marcada la casilla OLE público.
Para construir el COM DLL abra el proyecto y escoja la opción Información del proyecto... del menú Proyecto, seleccione la etiqueta Servidores y establezca sus opciones de la clase (estas opciones están bastante bien documentadas en el archivo de ayuda). Guarde esas opciones y entonces escoja Generar y seleccione la opción Servidor COM (dll) y pulse el botón Aceptar. Esto va a generar el COM DLL con su clase COM servidor en él y registrará la DLL en su máquina. Para otros para usar la clase DLL necesitará ser instalada y registrada en sus máquinas, esto puede hacerse como parte del proceso de Instalación para su aplicación.
Una vez que usted ha hecho esto puede modificar sus formularios de VFP para que usen la clase COM. Simplemente cambie el evento Load para que sea:
THISFORM.oBusObj = NewObject("BusObj.Customer")
Donde BusObj es el nombre del archivo DLL que usted creó.
Usando Múltiples Interfaces del Usuario en el Diseño N-Tier
La ventaja de este diseño es que el mismo objeto de negocio puede ser usado por múltiples interfaces del usuario diferentes permitiendo una definición de clase para controlar el acceso a datos para cada UI que su sistema usa. Aunque su aplicación sólo pueda limitarse a formularios de VFP al principio, siguiendo este diseño N-Tier le permitirá, de manera más fácil, agregar otras interfaces al sistema en el futuro (haciendo el sistema escalable).
Usando Microsoft Excel para la Interface del Usuario
Aquí hay un ejemplo de una macro de Microsoft Excel que usa la misma clase de negocio para llenar con datos una hoja de cálculo.
Option Explicit
Public Dummy As Variant
Public oCustomer As Object
Sub nTier()
' nTier Macro
'
Dim lnRet As Integer
Set oCustomer = CreateObject("BusObj.Customer")
ActiveSheet.Cells(1, 1) = "Store ID"
ActiveSheet.Cells(1, 2) = "Store Name"
ActiveSheet.Cells(1, 3) = "Store City"
lnRet = Refresh()
' frmRefresh.Show
End Sub
Public Function Refresh()
' nTier Macro
' Refreshes the contents of the business logic object and the sheet.
Dim lnRet As Integer
Dim lnRow As Integer
lnRet = oCustomer.MoveFirst()
lnRow = 2
Do While lnRet > 0
ActiveSheet.Cells(lnRow, 1) = oCustomer.GetValue("store_id")
ActiveSheet.Cells(lnRow, 2) = oCustomer.GetValue("store_name")
ActiveSheet.Cells(lnRow, 3) = oCustomer.GetValue("store_city")
lnRet = oCustomer.MoveNext()
lnRow = lnRow + 1
Loop
Refresh = 1
End Function
Esta macro creará una instancia de la clase customer y entonces llenará las filas y columnas de la hoja con datos del objeto de negocio.
Resumen
Hay muchas charlas alrededor del diseño de sistemas N-Tier. Algunas personas son fuertes defensoras de usar N-Tier para todo, en cambio otras personas sienten que el N-Tier es excesivo en muchos lugares. Mi opinión es que algo que yo pueda hacer para mejorar con el tiempo un sistema para mi cliente es bien esfuerzo bien hecho.
El diseño N-Tier me da la capacidad de manejar datos a través del uso de clases de las que se pueden hacer subclases y pueden ser especializadas dentro de VFP y al mismo tiempo pueden hacer que esas clases estén disponibles a otras herramientas de desarrollo que manteniendo el lugar solo modificando el acceso de datos. Esta sola situación es uno de los mayores beneficios logrados a través del diseño N-Tier. Si el cliente cambia el servidor de la base de datos a otra, hay sólo un lugar para hacer los cambios necesarios y todas las interfaces del usuario se actualizarán.
Envíe un email a Jim Booth con preguntas o comentarios
sobre esta información.