¿Cómo encaja una capa de servicio en mi implementación de repositorio?


He creado una clase de modelo POCO y una clase de repositorio que maneja la persistencia. Dado que POCO no puede acceder al repositorio, hay muchas tareas de lógica de negocio 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 interfaz de usuario y la capa de repositorio. De lo que no estoy seguro es exactamente cómo se supone que funcione...

Además de la capa de servicio, también debe haber un negocio separado capa lógica, o es que el papel de la capa de servicio?

¿Debería haber un servicio por repositorio?

¿Es la capa de servicio la única forma en que la interfaz de usuario puede instigar 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 comprobar para asegurarse de que una entrada es válida y que existe un elemento a actualizar en la base de datos antes de actualizar?

Puede el el modelo, el repositorio y la interfaz de usuario hacen 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 classic ADO.NET. Espero pasar a MVC y EF4 finalmente.

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 forma en que la interfaz de usuario u otros servicios pueden comunicarse con el repositorio y contendrá cualquier validación que no quepa en el objeto de dominio (por ejemplo, validaciones que necesitan 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. ¿Añado las mismas (y posiblemente más) propiedades al servicio como lo tengo en mi modelo (cantidad, código, etc) o solo ofrezco métodos que aceptan objetos GiftCertificate y parámetros directos?

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

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

  4. Está bien que la interfaz de usuario 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 hacerlo cumplir?

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

Además, si quiero canjear desde la capa de IU, primero llamo a los métodos de validación del modelo/servicio desde la IU para dar retroalimentación al usuario y luego llamo al método de Canjeo que ejecutará las mismas comprobaciones de nuevo internamente?

Ejemplo para llamar al servicio para realizar una operación de canje desde UI:

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 GCS y cómo se separan las validaciones entre entidad/servicio. El usuario/consumidor no debe tener que preocuparse por qué validaciones están en qué lugar... ¿consejo?

Author: jpshook, 2011-05-26

3 answers

Echa un vistazo a S#arp Architeture . Es como un marco arquitectónico de mejores prácticas para la construcción ASP.NET Aplicaciones MVC. El patrón de arquitectura general 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 de negocio 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 negocio separada, o esa es la función de la capa de servicio?

Los modelos deben ser responsables de la validación a nivel de campo (ej. uso de atributos de campo obligatorios), mientras que los controladores pueden validar los datos antes de guardarlos (ej. comprobar 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 significaba eso).

¿Es la capa de servicio la única forma en que la interfaz de usuario puede instigar 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 Usando DTOs:

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

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

Los modelos validan los valores a nivel de campo ex. asegurarse de que la entrada sea válida comprobando los campos obligatorios, los rangos de edad o fecha, etc. Los servicios deben hacer cualquier validación necesaria que requiera comprobación fuera del valor del modelo ex. Comprobación de que el certificado de regalo no se ha canjeado todavía, comprobación propiedades de la tienda para la que está el certificado de regalo).

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

Los controladores y otros servicios deberían ser los únicos que realicen 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 son todos métodos estáticos?

Pueden ser pero es más fácil de mantener y extender si 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 respondió arriba.

ACTUALIZACIÓN

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

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

Usted necesita mirar los servicios exactamente como lo que su nombre implica - acciones que los controladores pueden invocar. No necesitará propiedades definidas en su modelo, ya que ya están disponibles en el modelo.

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

Los controladores y los servicios deben tener campos privados para servicios y repositorios respectivamente. No deberías 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 también (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 entonces eso implica que solo le interesa cierta información.

Para la validación puedo ver por qué estás un poco preocupado ya que hay validación hecha directamente en el modelo y otros tipos de validación hecha en servicios. La regla general que he usado es que si la validación requiere llamadas a la base de datos, entonces debe hacerse en la capa de servicio.

Está bien que la interfaz de usuario simplemente cree un nuevo objeto GiftCertificate directamente sin pasar por el servicio (por ejemplo, el parámetro to call métodos de validación de la entidad). Si no, ¿cómo hacerlo cumplir?

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

Para estas 2 preguntas vamos a ir a través de un escenario:

El usuario introduce información para crear una nueva certificado y presenta. Hay validación a nivel de campo, por lo que si un cuadro de texto es nulo o si la cantidad en dólares es negativa, 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 negocio, como si la tienda ya ha emitido demasiados certificados de regalo. Devuelve una enumeración para el estado si hay varios códigos de error o lanza una excepción con la información de error.

Finalmente, el llamadas de servicio gcRepository.Save(gc).

 41
Author: Lester,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-09 20:14:04
  1. No tienes que crear repositorio por entidad, ver aquí para más ,

    Normalmente se define un repositorio por agregar en el dominio. Es decir: nosotros no tiene un repositorio por entidad! Si echamos un vistazo a una entrada de orden simple sistema el Orden de la entidad podría ser el raíz de un agregado de Orden. Así nosotros tendrá un Repositorio de Pedidos.

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

  3. El Servicio crea la instancia del Modelo, el repositorio nunca interactuará con el Modelo, de hecho devuelve la Entidad que el modelo usará posteriormente.

  4. Manejar la entrada / rango, etc. tipo de validación a nivel de interfaz de usuario (u puede usar javascript o cualquier otra biblioteca), y dejar que los Servicios manejen solo los aspectos comerciales. Usted puede obtener los beneficios de Atributos que will hace lo mismo.

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


Cambia el código,

  1. Separe el Modelo y los Repositorios.

    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)
    
    }
    
 3
Author: paragy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-26 18:08:16

Puede crear un servicio llamado GiftCertificateService.

De esa manera coordinará cualquier tarea que no pertenezca a la responsabilidad del modelo GiftCertificateModel en su servicio. (No debe confundirse con un servicio WCF).

El servicio controlará todas las tareas para que su IU (o cualquier persona que llame) use los métodos definidos en el servicio.

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

Para ex. (basado en el código de ejemplo 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 interfaz de usuario llamará al método CreateCertificate (pasando argumentos, etc.) y el método puede devolver algo también.

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

 0
Author: Nikos Baxevanis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-26 13:49:22