work unit unidades trabajo repositorio patrón patron parte net implementación generico framework ejemplo and c# asp.net domain-driven-design repository repository-pattern

c# - unidades - repository and unit of work



¿Cómo encaja una capa de servicio en la implementación de mi repositorio? (3)

  1. No tiene que crear un repositorio por entidad, consulte aquí para obtener más información ,

    Por lo general, uno define un repositorio por agregado en el dominio. Es decir: ¡no tenemos un repositorio por entidad! Si observamos un sistema de entrada de órdenes simple, la entidad Order podría ser la raíz de un agregado de Orden. Así tendremos un repositorio de pedidos.

  2. ¿Debería haber un servicio por repositorio? -> No siempre, ya que puede usar múltiples repositorios en un servicio.

  3. El servicio crea la instancia del modelo, el repositorio nunca interactuará con el modelo, de hecho, devuelve la entidad que el modelo utilizará posteriormente.

  4. Manejar el tipo de validación de entrada / rango, etc. a nivel de UI (u puede usar javascript o cualquier otra biblioteca), y permitir que los Servicios manejen solo aspectos comerciales. Puede obtener los beneficios de los atributos que harán lo mismo.

  5. UI-> Service-> Repository, si el repositorio está llamando al servicio, entonces thr debe ser algo IMO incorrecto.

Tu código cambia,

  1. Hacer modelo y repositorios separados.

    public class GiftCertificateModel { } public class GiftCertificateRepository { //Remove Model related code from here, and just put ONLY database specific code here, (no business logic also). Common methods would be Get, GetById, Insert, Update etc. Since essence of Repository is to have common CRUD logic at one place soyou don''t have to write entity specific code. You will create entity specific repository in rare cases, also by deriving base repository. } public class GiftCertificateService() { //Create Model instance here // Use repository to fill the model (Mapper) }

He creado una clase de modelo POCO y una clase de repositorio que maneja la persistencia. Como el POCO no puede acceder al repositorio, hay muchas tareas de lógica de negocios en el repositorio que no parecen correctas. Por lo que he leído, parece que necesito una capa de servicio que se encuentra entre los consumidores de UI y la capa de repositorio. De lo que no estoy seguro es exactamente cómo se supone que funciona ...

Además de la capa de servicio, ¿debería haber también una capa de lógica de negocios separada o es el rol de la capa de servicio?

¿Debería haber un servicio por repositorio?

¿Es la capa de servicio la única forma en que la IU puede instanciar los objetos del modelo o el repositorio proporciona la nueva instancia del modelo al servicio?

¿Pongo mi parámetro, modelo y otras validaciones en la capa de servicio que hacen cosas como verificar para asegurar que una entrada es válida y que existe un elemento para actualizar en la base de datos antes de actualizar?

¿Pueden el modelo, el repositorio y la interfaz de usuario hacer llamadas a la capa de servicio, o es solo para que la interfaz de usuario consuma?

¿Se supone que la capa de servicio es todos los métodos estáticos?

¿Cuál sería una forma típica de llamar a la capa de servicio desde la interfaz de usuario?

¿Qué validaciones deben estar en el modelo frente a la capa de servicio?

Aquí hay un código de ejemplo para mis capas existentes:

public class GiftCertificateModel { public int GiftCerticiateId {get;set;} public string Code {get;set;} public decimal Amount {get;set;} public DateTime ExpirationDate {get;set;} public bool IsValidCode(){} } public class GiftCertificateRepository { //only way to access database public GiftCertificateModel GetById(int GiftCertificateId) { } public List<GiftCertificateModel> GetMany() { } public void Save(GiftCertificateModel gc) { } public string GetNewUniqueCode() { //code has to be checked in db } public GiftCertificateModel CreateNew() { GiftCertificateModel gc = new GiftCertificateModel(); gc.Code = GetNewUniqueCode(); return gc; } }

ACTUALIZACIÓN: Actualmente estoy usando formularios web y ADO.NET clásico. Espero pasar a MVC y EF4 eventualmente.

ACTUALIZACIÓN: Muchas gracias a @Lester por su gran explicación. Ahora entiendo que necesito agregar una capa de servicio para cada uno de mis repositorios. Esta capa será la ÚNICA manera en que la UI u otros servicios pueden comunicarse con el repositorio y contendrá cualquier validación que no se ajuste al objeto del dominio (por ejemplo, validaciones que deben llamar al repositorio)

public class GiftCertificateService() { public void Redeem(string code, decimal amount) { GiftCertificate gc = new GiftCertificate(); if (!gc.IsValidCode(code)) { throw new ArgumentException("Invalid code"); } if (amount <= 0 || GetRemainingBalance(code) < amount) { throw new ArgumentException("Invalid amount"); } GiftCertificateRepository gcRepo = new GiftCertificateRepository(); gcRepo.Redeem(code, amount); } public decimal GetRemainingBalance(string code) { GiftCertificate gc = new GiftCertificate(); if (!gc.IsValidCode(code)) { throw new ArgumentException("Invalid code"); } GiftCertificateRepository gcRepo = new GiftCertificateRepository(); gcRepo.GetRemainingBalance(code); } public SaveNewGC(GiftCertificate gc) { //validates the gc and calls the repo save method //updates the objects new db ID } }

Preguntas

  1. ¿Agrego las mismas (y posiblemente más) propiedades al servicio que tengo en mi modelo (cantidad, código, etc.) o solo ofrezco métodos que acepten objetos GiftCertificate y parámetros directos?

  2. ¿Debo crear una instancia predeterminada de la entidad GiftCertificate cuando se llama al constructor del servicio o simplemente crear otras nuevas según sea necesario (por ejemplo, para los métodos de validación en el servicio que necesitan llamar a los métodos de validación en la entidad? También, la misma pregunta sobre la creación de un valor predeterminado instancia de repositorio ...?

  3. Sé que expongo la funcionalidad del repositorio a través del servicio, ¿también expongo los métodos de la entidad (por ejemplo, IsValidCode, etc.)?

  4. Está bien que la UI simplemente cree un nuevo objeto GiftCertificate directamente sin pasar por el servicio (por ejemplo, para llamar a los métodos de validación de parámetros desde la entidad). Si no, ¿cómo hacerla cumplir?

  5. En la capa de la interfaz de usuario, cuando quiero crear un nuevo certificado de regalo, ¿llamo las validaciones del modelo / servicio (como IsValidExpirationDate, etc.) directamente desde la capa de la interfaz de usuario O HAGO primero el objeto, luego lo paso para validarlo y ¿Luego devuelve algún tipo de resumen de validación a la interfaz de usuario?

Además, si quiero Canjear desde la capa de UI, ¿primero llamo a los métodos de validación del modelo / servicio desde la UI para dar retroalimentación al usuario y luego llamo al método de Canjear que ejecutará las mismas comprobaciones de forma interna?

Ejemplo para llamar al servicio para hacer una operación de Canje desde la interfaz de usuario:

string redeemCode = RedeemCodeTextBox.Text; GiftCertificateService gcService = new GiftCertificateService(); GiftCertificate gc = new GiftCertificate(); //do this to call validation methods (should be through service somehow?) if (!gc.IsValid(redeemCode)) { //give error back to user } if (gcService.GetRemainingBalance(redeemCode) < amount) { //give error back to user } //if no errors gcService.Redeem(code,amount);

Ejemplo para crear un nuevo certificado de regalo desde la interfaz de usuario:

GiftCertificateService gcService = new GiftCertificateService(); GiftCertificate gc = new GiftCertificate(); if (!gc.IsValidExpDate(inputExpDate)) { //give error to user.. } //if no errors... gc.Code = gcService.GetNewCode(); gc.Amount = 10M; gc.ExpirationDate = inputExpDate; gcService.SaveNewGC(gc); //method updates the gc with the new id...

Algo se siente mal sobre la forma en que se crean los GC y cómo se separan las validaciones entre entidad / servicio. El usuario / consumidor no debería tener que preocuparse por las validaciones en qué lugar ... ¿consejos?


Echa un vistazo a S # arp Architeture . Es como un marco arquitectónico de mejores prácticas para la creación de aplicaciones MVC de ASP.NET. El patrón general de la arquitectura es tener 1 repositorio por entidad que es responsable solo del acceso a los datos y 1 servicio por repositorio que es responsable solo de la lógica empresarial y la comunicación entre controladores y servicios.

Para responder a sus preguntas basadas en S # arp Architeture:

Además de la capa de servicio, ¿debería haber también una capa de lógica de negocios separada o es el rol de la capa de servicio?

Los modelos deben ser responsables de la validación a nivel de campo (por ejemplo, utilizando los atributos de campo requeridos) mientras que los controladores pueden validar los datos antes de guardar (por ejemplo, verificar el estado antes de guardar).

¿Debería haber una capa de servicio por repositorio?

Sí, debería haber un servicio por repositorio (no 1 capa de servicio por repositorio, pero supongo que quiso decir eso).

¿Es la capa de servicio la única forma en que la IU puede instanciar los objetos del modelo o el repositorio proporciona la nueva instancia del modelo al servicio?

Los repositorios y servicios pueden devolver una sola entidad, una colección de entidades u objetos de transferencia de datos (DTO) según sea necesario. Los controladores pasarán estos valores a un método constructor estático en el modelo que devolverá una instancia del modelo.

ex utilizando DTOs:

GiftCertificateModel.CreateGiftCertificate(int GiftCerticiateId, string Code, decimal Amount, DateTime ExpirationDate)

¿Pongo mi parámetro, modelo y otras validaciones en la capa de servicio que hacen cosas como verificar para asegurar que una entrada es válida y que existe un elemento para actualizar en la base de datos antes de actualizar?

Los modelos validan valores de nivel de campo ej. asegurarse de que la entrada sea válida al verificar los campos requeridos, la edad o los rangos de fechas, etc. Los servicios deben hacer cualquier validación necesaria que requiera una verificación fuera del valor del modelo, por ejemplo. Verificando que el certificado de regalo aún no haya sido canjeado, verificando las propiedades de la tienda para la cual se encuentra el certificado de regalo)

¿Pueden el modelo, el repositorio y la interfaz de usuario hacer llamadas a la capa de servicio, o es solo para que la interfaz de usuario consuma?

Los controladores y otros servicios deben ser los únicos que realizan llamadas a la capa de servicio. Los servicios deben ser los únicos que hacen llamadas a los repositorios.

¿Se supone que la capa de servicio es todos los métodos estáticos?

Pueden ser, pero es más fácil de mantener y extender si no lo son. Los cambios en las entidades y la adición / eliminación de subclases son más fáciles de cambiar si hay 1 servicio por entidad / subclase.

¿Cuál sería una forma típica de llamar a la capa de servicio desde la interfaz de usuario?

Algunos ejemplos de controladores que llaman a la capa de servicio:

giftCertificateService.GetEntity(giftCertificateId); (which in turn is just a call to the giftCertificateRepository.GetEntity(giftCertificateId) giftCertificateService.Redeem(giftCertificate);

¿Qué validaciones deben estar en el modelo frente a la capa de servicio?

Ya respondí arriba.

ACTUALIZAR

Ya que está utilizando WebForms, puede ser un poco más difícil comprender algunos de los conceptos, pero todo lo que he mencionado es aplicable, ya que lo que estoy describiendo es un paradigma general de MVC. ADO.NET para el acceso a datos no importa, ya que el acceso a los datos se desacopla a través de repositorios.

¿Agrego las mismas (y posiblemente más) propiedades al servicio que tengo en mi modelo (cantidad, código, etc.) o solo ofrezco métodos que acepten objetos GiftCertificate y parámetros directos?

Debe considerar los servicios como lo que su nombre indica, acciones que los controladores pueden invocar. No necesitará propiedades que estén definidas en su modelo ya que ya están disponibles en el modelo.

¿Debo crear una instancia predeterminada de la entidad GiftCertificate cuando se llama al constructor del servicio o simplemente crear otras nuevas según sea necesario (por ejemplo, para los métodos de validación en el servicio que necesitan llamar a los métodos de validación en la entidad? También, la misma pregunta sobre la creación de un valor predeterminado instancia de repositorio ...?

Los controladores y servicios deben tener campos privados para servicios y repositorios, respectivamente. No debes crear instancias para cada acción / método.

Sé que expongo la funcionalidad del repositorio a través del servicio, ¿también expongo los métodos de la entidad (por ejemplo, IsValidCode, etc.)?

No estoy muy seguro de lo que quieres decir aquí. Si los servicios devuelven entidades, entonces esos métodos en las entidades ya están expuestos. Si devuelven DTO, eso significa que está interesado solo en cierta información.

Para la validación puedo ver por qué está un poco preocupado, ya que la validación se realiza directamente en el modelo y otros tipos de validación se realizan en los servicios. La regla de oro que he usado es que si la validación requiere llamadas a la base de datos, entonces debería hacerse en la capa de servicio.

Está bien que la UI simplemente cree un nuevo objeto GiftCertificate directamente sin pasar por el servicio (por ejemplo, para llamar a los métodos de validación de parámetros desde la entidad). Si no, ¿cómo hacerla cumplir?

En la capa de la interfaz de usuario, cuando quiero crear un nuevo certificado de regalo, ¿llamo las validaciones del modelo / servicio (como IsValidExpirationDate, etc.) directamente desde la capa de la interfaz de usuario O HAGO primero el objeto, luego lo paso para validarlo y ¿Luego devuelve algún tipo de resumen de validación a la interfaz de usuario?

Para estas 2 preguntas vamos a pasar por un escenario:

El usuario ingresa información para crear un nuevo certificado y lo envía. Existe una validación a nivel de campo, por lo que si un cuadro de texto es nulo o si el monto en dólares es negativo, se produce un error de validación. Suponiendo que todos los campos son válidos, el controlador llamará al servicio gcService.Save(gc) .

El servicio comprobará otra lógica de negocios, como si la tienda ya ha emitido demasiados certificados de regalo. O bien devuelve una enumeración para el estado si hay varios códigos de error o lanza una excepción con la información del error.

Finalmente, el servicio llama a gcRepository.Save(gc) .


Puede crear un servicio llamado GiftCertificateService.

De esa forma, coordinará cualquier tarea que no sea responsabilidad de GiftCertificateModel en su servicio. (No debe confundirse con un servicio WCF).

El servicio controlará todas las tareas, de modo que su UI (o la persona que llame que sea) usará los métodos definidos en el servicio.

El servicio llamará a los métodos en el modelo, usará el repositorio, creará transacciones, etc.

Por ej. (basado en el código de muestra que proporcionó):

public class GiftCertificateService { public void CreateCertificate() { //Do whatever needs to create a certificate. GiftCertificateRepository gcRepo = new GiftCertificateRepository(); GiftCertificateModel gc = gcRepo.CreateNew(); gc.Amount = 10.00M; gc.ExpirationDate = DateTime.Today.AddMonths(12); gc.Notes = "Test GC"; gcRepo.Save(gc); } }

La IU llamará al método CreateCertificate (pasando argumentos, etc.) y el método también puede devolver algo.

NOTA: Si desea que la clase actúe en la interfaz de usuario, cree una clase de controlador (si está haciendo MVC) o una clase de presentador (si está haciendo MVVM, y no quiere poner todo dentro del ViewModel) y use el GiftCertificateService de esa clase.