Parallel Change es una técnica de refactoring que nos permite implementar cambios incompatibles en una interfaz de manera segura.
Vamos a ver un ejemplo para entender mejor esta técnica. Tenemos un UserRepository
que nos permite buscar por nombre.
public class UserRepository : Repository, IUserRepository { public User FindOneByName(UserName name) { using (var scope = _scopeFactory.CreateScope()) { var dbContext = scope.ServiceProvider.GetRequiredService(); return dbContext.Users.FirstOrDefault(c => c.name.GetValue() == name.GetValue()); } } }
Desde negocio nos piden que, en otro caso de uso, podamos buscar un usuario por email. Por lo tanto, añadimos un nuevo método al UserRepository
.
public class UserRepository : Repository, IUserRepository { public User FindOneByName(UserName name) { using (var scope = _scopeFactory.CreateScope()) { var dbContext = scope.ServiceProvider.GetRequiredService(); return dbContext.Users.FirstOrDefault(c => c.name.GetValue() == name.GetValue()); } } public User FindOneByEmail(UserEmail email) { using (var scope = _scopeFactory.CreateScope()) { var dbContext = scope.ServiceProvider.GetRequiredService(); return dbContext.Users.FirstOrDefault(c => c.email.GetValue() == email.GetValue()); } } }
Esta solución puede parecer la más adecuada, pero si en un futuro queremos buscar por otros atributos del usuario tendríamos que añadir más métodos al UserRepositorio
. Lo que nos llevaría a violar seguramente el principio Open/Closed de SOLID.
La solución más adecuada sería añadir un método que nos permita buscar por cualquier atributo que tenga el usuario.
public List FindByCriteria(Criteria criteria) { using (var scope = _scopeFactory.CreateScope()) { //CODE } }
Ahora bien, el problema surge a la hora de cambiar todos aquellos clientes que utilicen el método FindOneByName
al nuevo método FindByCriteria
. Ya que según el contexto y el código hacer estos cambios puede llegar a ser muy complejos. Entonces en lugar de cambiar todos los clientes, decidimos aplicar la técnica Parallel Change. Para poder aplicarla, debemos hacerlo paulatinamente siguiendo sus tres frases: expand, migrate y contract.
Expand:
Esta fase consiste en añadir un nuevo método de manera que la interface actual contiene la versión antigua y la nueva. Con lo cual, los clientes antiguos continuan utilizando la versión antigua
(FindOneByName) y los nuevos comenzarán a utilizar la versión nueva (FindByCriteria). Por consiguiente, el UserRepositorio
nos quedaría de la siguiente manera:
public class UserRepository : Repository, IUserRepository { public User FindOneByName(UserName name) { using (var scope = _scopeFactory.CreateScope()) { var dbContext = scope.ServiceProvider.GetRequiredService(); return dbContext.Users.FirstOrDefault(c => c.name.GetValue() == name.GetValue()); } } public List FindByCriteria(Criteria criteria) { using (var scope = _scopeFactory.CreateScope()) { //CODE } } }
Migrate:
Durante esta fase, se debe actualizar todos los clientes que estaban llamando a la versión antigua a la nueva versión. Es recomendable hacerlo de forma progresiva si no disponemos de mucho tiempo y marcar la versión antigua como deprecada. De esta forma, evitamos que nuevos clientes utilicen la versión antigua.
Contract:
Se puede procede a eliminar la versión antigua una vez que todos los clientes hayan migrado a la nueva versión.
Conclusión
Esta técnica nos permite subir nuestro código a producción en cualquiera de las fases. Además, reduce el riesgo de la migración de los clientes ya que se hace de manera progresiva y con sus respectivos tests.
- admin publicó hace 3 años
- último editado hace 3 años
- Debes iniciar sesión para publicar comentarios
Por favor, primero debes inicia sesión para enviar.