Implementar el patrón de repositorio es una práctica muy común en aquellos proyectos que necesitan trabajar con datos externos, ya sea esto a través de bases de datos, servicios web, archivos u otros.
Este patrón nos ayuda mejorar la mantenibilidad y la capacidad de prueba. Con la correcta separación de intereses, nuestra lógica de negocio ya se puede concentrar en el negocio en sí y no preocuparse en operaciones de bajo nivel, como abrir una conexión a una base de datos o algún socket.
Este patrón nos habla de una capa intermedia entre la lógica de negocio y la fuente de datos, pero hay muchas formas de entenderlo así que exploremos algunos puntos.
Este patrón nos ayuda mejorar la mantenibilidad y la capacidad de prueba. Con la correcta separación de intereses, nuestra lógica de negocio ya se puede concentrar en el negocio en sí y no preocuparse en operaciones de bajo nivel, como abrir una conexión a una base de datos o algún socket.
Este patrón nos habla de una capa intermedia entre la lógica de negocio y la fuente de datos, pero hay muchas formas de entenderlo así que exploremos algunos puntos.
1. Reponsabilidad única
En el siguiente programa estamos accediendo a una base de datos para obtener un listado y también para mostrarlo en pantalla, pero todo se encuentra en una sola clase violando el principio de diseño de responsabilidad única.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace RepPattern
{
internal class Program
{
private static void Main(string[] args)
{
DatabaseFactory.SetDatabaseProviderFactory(new DatabaseProviderFactory(), false);
RunApplication();
Console.ReadKey();
}
private static async void RunApplication()
{
var entities = await GetEntities();
foreach (var item in entities)
Console.WriteLine("Id: {0}, Names: {1}", item.Id, item.Names);
}
private static Task<List<MyEntity>> GetEntities()
{
var taskCompletionSource = new TaskCompletionSource<List<MyEntity>>();
var database = DatabaseFactory.CreateDatabase();
database.BeginExecuteReader("[dbo].[myTable_sel]", (asyncResult) =>
{
try
{
var entities = new List<MyEntity>();
var db = (Database)asyncResult.AsyncState;
using (var reader = db.EndExecuteReader(asyncResult))
{
var a = reader.GetOrdinal("Id");
var b = reader.GetOrdinal("Names");
var c = reader.GetOrdinal("Address");
var d = reader.GetOrdinal("Email");
var e = reader.GetOrdinal("Code");
var f = reader.GetOrdinal("Lat");
var g = reader.GetOrdinal("Lng");
while (reader.Read())
{
entities.Add(new MyEntity
{
Id = reader.GetInt32(a),
Names = reader.GetString(b),
Address = reader.GetString(c),
Email = reader.GetString(d),
Code = reader.GetString(e),
Lat = reader.GetDouble(f),
Lng = reader.GetDouble(g)
});
}
}
taskCompletionSource.TrySetResult(entities);
}
catch (Exception ex)
{
taskCompletionSource.TrySetException(ex);
}
}, database);
return taskCompletionSource.Task;
}
}
}
Lo ideal sería mover la lógica correspondiente a la conexión a base de datos a una una clase aparte en otro proyecto de librería de clases.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace RepPattern.Repository
{
public class MyEntityRepository
{
public Task<List<MyEntity>> GetEntities()
{
var taskCompletionSource = new TaskCompletionSource<List<MyEntity>>();
var database = DatabaseFactory.CreateDatabase();
database.BeginExecuteReader("[dbo].[myTable_sel]", (asyncResult) =>
{
try
{
var entities = new List<MyEntity>();
var db = (Database)asyncResult.AsyncState;
using (var reader = db.EndExecuteReader(asyncResult))
{
var a = reader.GetOrdinal("Id");
var b = reader.GetOrdinal("Names");
var c = reader.GetOrdinal("Address");
var d = reader.GetOrdinal("Email");
var e = reader.GetOrdinal("Code");
var f = reader.GetOrdinal("Lat");
var g = reader.GetOrdinal("Lng");
while (reader.Read())
{
entities.Add(new MyEntity
{
Id = reader.GetInt32(a),
Names = reader.GetString(b),
Address = reader.GetString(c),
Email = reader.GetString(d),
Code = reader.GetString(e),
Lat = reader.GetDouble(f),
Lng = reader.GetDouble(g)
});
}
}
taskCompletionSource.TrySetResult(entities);
}
catch (Exception ex)
{
taskCompletionSource.TrySetException(ex);
}
}, database);
return taskCompletionSource.Task;
}
}
}
Y terminar con la clase principal de la siguiente forma:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace RepPattern
{
internal class Program
{
private static void Main(string[] args)
{
DatabaseFactory.SetDatabaseProviderFactory(new DatabaseProviderFactory(), false);
RunApplication();
Console.ReadKey();
}
private static async void RunApplication()
{
var entities = await new MyEntityRepository().GetEntities();
foreach (var item in entities)
Console.WriteLine("Id: {0}, Names: {1}", item.Id, item.Names);
}
}
}
2. Repositorios genéricos
En muchas operaciones con datos, creamos, modificamos, eliminamos o consultamos registros, por ello es usual considerar la creación de una interfaz para un repositorio genérico.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace RepPattern.Repository
{
public interface IRepository<T>
{
void Create(T entity);
List<T> ReadAll();
T Find(int id);
void Update(T entity);
void Delete(int id);
}
}
Sin embargo, esta idea nos puede llevar a encontrarnos con situaciones muy parecidas a estas:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.Practices.EnterpriseLibrary.Data;
using System;
using System.Collections.Generic;
namespace RepPattern.Repository
{
public class MonthlyBalanceRepository : IRepository<MonthlyBalance>
{
public void Create(MonthlyBalance entity)
{
throw new NotImplementedException();
}
public void Delete(int id)
{
throw new NotImplementedException();
}
public MonthlyBalance Find(int id)
{
MonthlyBalance result = null;
using (var reader = DatabaseFactory.CreateDatabase()
.ExecuteReader("[sales].[balance_find_bymonth]", id))
{
if (reader.Read())
result = new MonthlyBalance
{
//...
};
}
return result;
}
public List<MonthlyBalance> ReadAll()
{
var result = new List<MonthlyBalance>();
using (var reader = DatabaseFactory.CreateDatabase()
.ExecuteReader("[sales].[balance_list_bymonth]"))
{
while (reader.Read())
result.Add(new MonthlyBalance
{
//...
});
}
return result;
}
public void Update(MonthlyBalance entity)
{
throw new NotImplementedException();
}
}
}
No es que se acabe el mundo, pero hay que tomarlo en consideración ya que podría dificultar el diseño de nuestras aplicaciones y viola un principio de diseño.
3. Segregación de interfaces
Implementar las interfaces adecuadamente nos ayuda a tener mejores resultados. Si creamos una interfaz como esta:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace RepPattern.Repository.Base
{
public interface IMyEntityRepository
{
Task<List<MyEntity>> GetEntities();
}
}
Tendremos la capacidad de trabajar con diferentes implementaciones según la ocasión.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace RepPattern.Repository
{
public class MyEntityMockRepository : IMyEntityRepository
{
public Task<List<MyEntity>> GetEntities()
{
var json = File.ReadAllText("entities.json");
return Task.FromResult(JsonConvert.DeserializeObject<List<MyEntity>>(json));
}
}
}
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace RepPattern.Repository
{
public class MyEntitySqlRepository : IMyEntityRepository
{
public Task<List<MyEntity>> GetEntities()
{
var taskCompletionSource = new TaskCompletionSource<List<MyEntity>>();
var database = DatabaseFactory.CreateDatabase();
database.BeginExecuteReader("[dbo].[myTable_sel]", (asyncResult) =>
{
try
{
var entities = new List<MyEntity>();
var db = (Database)asyncResult.AsyncState;
using (var reader = db.EndExecuteReader(asyncResult))
{
var a = reader.GetOrdinal("Id");
var b = reader.GetOrdinal("Names");
var c = reader.GetOrdinal("Address");
var d = reader.GetOrdinal("Email");
var e = reader.GetOrdinal("Code");
var f = reader.GetOrdinal("Lat");
var g = reader.GetOrdinal("Lng");
while (reader.Read())
{
entities.Add(new MyEntity
{
Id = reader.GetInt32(a),
Names = reader.GetString(b),
Address = reader.GetString(c),
Email = reader.GetString(d),
Code = reader.GetString(e),
Lat = reader.GetDouble(f),
Lng = reader.GetDouble(g)
});
}
}
taskCompletionSource.TrySetResult(entities);
}
catch (Exception ex)
{
taskCompletionSource.TrySetException(ex);
}
}, database);
return taskCompletionSource.Task;
}
}
}
Y en nuestro programa principal solo nos preocupariamos de la interface y de como se realizaría la instanciación que necesitamos.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace RepPattern
{
internal class Program
{
private static void Main(string[] args)
{
DatabaseFactory.SetDatabaseProviderFactory(new DatabaseProviderFactory(), false);
RunApplication();
Console.ReadKey();
}
private static async void RunApplication()
{
IMyEntityRepository entityRepository = new MyEntityMockRepository();
var entities = await entityRepository.GetEntities();
foreach (var item in entities)
Console.WriteLine("Id: {0}, Names: {1}", item.Id, item.Names);
}
}
}
Algunos extienden esto usando inyección de dependencias, otros se organizan usando espacios de nombre y otros hacen cosas diferentes. La resolución o complejidad de esto ya depende de los requisitos del proyecto o elección de cada uno.
No hay comentarios.:
Publicar un comentario