Hola:
Antes que nada es importante que sepas que no hay una sola forma de trabajar en capas, hay varias, y justamente eso es lo que permite poder implementar lo mismo de distintas formas, cada una de las cuales puede tener sus ventajas y sus desventajas dpendiendo de cómo estructures tu sistema y de qué tipo de funcionalidades elijas usar. No es fácil explicar todas estas opciones, pero voy a intentar dejarte algunos ejemplos con sus pro y contras y así de paso los demás también pueden comentar sobre los mismos ejemplos. Omito el control de errores para minimizar los ejemplos.
Ejemplo 1: Usando un objeto de datos temporal (compartible por parámetro)
Podrías tener una clase CL_DATOS_EDICION tipo custom con estas propiedades:
CODIGO
NOMBRE
DIRECCION
POBLACION
DNI
De la misma podrías hacerte un objeto oDatos, así:
thisform.oDatos = CreateObject("CL_DATOS_EDIDION")
Luego, en los ControlSource de los controles, podrías usar estas propiedades directamente:
txtCodigo.ControlSource = "thisform.oDatos.Codigo"
txtNombre.ControlSource = "thisform.oDatos.Nombre"
...
También podrías tener un clase de negocio CL_BUS como esta:
DEFINE CLASS CL_BUS AS Custom
PROCEDURE Init
* El negocio puede tener una referencia a la clase de datos (capa de datos)
this.oDatos = CreateObject("CL_DATOS")
ENDPROC
FUNCTION Codigo_Valido( tcCodigo )
RETURN NOT EMPTY( tcCodigo )
ENDFUNC
FUNCTION DNI_Valido( tcDNI, tcMsgError )
IF (validacion que compruebe el DNI valido)
tcMsgError = ""
RETURN .T.
ELSE
tcMsgError = "El dígito de control no es válido"
RETURN .F.
ENDIF
ENDFUNC
FUNCTION Datos_Relacionados( tcCodigo, toDatos )
RETURN THIS.oDatos.Datos_Relacionados( tcCodigo, @toDatos )
ENDFUNC
FUNCTION Calcular_Precio( tcCodArticulo, tnIVA )
LOCAL lnTotal, lnCosto
lnCosto = this.oDatosArt.Costo_Articulo( tcCodArticulo )
lnTotal = lnCosto * tnIva / 100 && Esto es muy simplificado, solo para el ejemplo
RETURN lnTotal
ENDFUNC
ENDDEFINE
Y en el form podrías tener una instancia del objeto de negocio que tiene las validaciones, calculos, etc:
thisform.oBus = CreateObject("CL_BUS")
La clase de datos (capa de datos) podría llamarse CL_DATOS y ser de tipo custom:
DEFINE CLASS CL_DATOS AS Custom
FUNCTION Datos_Relacionados( tcCodigo, toDatos )
LOCAL llEncontrado
SELECT * FROM LaTablaDeDatos.WHERE CODIGO = tcCodigo INTO ARRAY laDatos
IF _TALLY > 0
llEncontrado = .T.
toDatos.NOMBRE = laDatos(1,2)
toDatos.DIRECCION = laDatos(1,3)
toDatos.POBLACION = laDatos(1,4)
toDatos.DNI = laDatos(1,5)
ENDIF
RETURN llEncontrado
ENDFUNC
ENDDEFINE
Luego, cuando quieras hacer las validaciones (por ejemplo en el VALID() de los controles) podrías tener algo como esto:
PROCEDURE txtCodigo.VALID()
RETURN this.oBus.Codigo_Valido( thisform.oDatos.CODIGO )
ENDPROC
Se me está acabando la batería y no puedo enchufar ahora, así que resumo un poco.
Como ves, los datos en este ejemplo se toman siempre de las propiedades del objeto de datos de edición (oDatos), pero nunca de la propiedad VALUE del control!
Es importante en el form trabajar siempre con las propiedades del objeto de edición y no con los VALUE de los controles, porque los controles deben reflejar todo el tiempo los valores de estas propiedades.
Para los métodos de negocio, lo ideal es paras los datos separados, aunque dado el caso de tener que pasar muchos parámetros, puede ser mejor pasar un objeto de datos directamente.
Variantes que te permite este tipo de uso de objetos:
* Las validaciones de negocio se pueden invocar desde cualquier sitio, tanto formularios como desde el mismo negocio como de un proceso externo (automatización batch) como testing automatizado
* Al recibir los parámetros de los datos necesarios para trabajar, el método de negocio está encapsulado y es fácil de mantener
* El formulario se puede reemplazar por cualquier otro método que complete datos, por ejemplo un archivo con datos preconfigurados que se entregan directamente al negocio
* Los cambios de reglas de negocio las hacés solamente en la clase de negocio. No tenés que modificar ningún formulario (salvo que el cambio sea estético y no de validación)
* El formulario podría tener su propio objeto de negocio "visual" para controlar las actualizaciones de pantalla. Sirve para centralizar ciertas funcionalidades visuales que a veces se repiten entre formularios, entonces se encapsulan en una libreria de negocio "visual" (reglas visuales que no tienen nada que ver con las de negocio)
EN fin, te dejo esta idea y en otro momento veo si puedo poner otro ejemplo distinto.
Saludos!