Implementación de patrón de repositorio en Python?
repository-pattern (2)
Principalmente por curiosidad, estoy buscando un marco de Python o un ejemplo para el Patrón de Repositorio de la lógica de desacoplamiento de la lógica de dominio.
El nombre "Patrón de repositorio" aparece en la publicación " Desentrañar la lógica de persistencia y dominio con el curador " (Ruby); la idea proviene de una section del libro "Diseño impulsado por dominio" y de Martin Fowler . La clase del modelo no contiene lógica de persistencia, más bien la aplicación declara las subclases del repositorio cuyas instancias actúan como colecciones en memoria de instancias del modelo. Cada repositorio mantiene el modelo de diferentes maneras, por ejemplo, a SQL (varias convenciones de esquema), a Riak u otro noSQL y a la memoria (para el almacenamiento en caché). Las convenciones marco significan que las subclases de repositorio generalmente requieren un código mínimo: simplemente declarar la subclase "WidgetRepository" de SQLRepository proporcionaría una colección que persiste el Widget del modelo en la tabla de base de datos denominada "widgets" y unir las columnas con los atributos del Widget.
Diferencias de otros patrones:
Patrón de registro activo : por ejemplo, Django ORM. La aplicación define solo la clase de modelo con lógica de dominio y algunos metadatos para la persistencia. El ORM agrega lógica de persistencia a la clase del modelo. Esto mezcla el dominio y la persistencia en una clase (indeseable según la publicación).
Gracias a @marcin veo que cuando Active Record soporta diversos backends y la función .save (con = "other_database"), eso le da el beneficio de múltiples backends del patrón de repositorio.
Así que, en cierto sentido, el patrón de repositorio es como un registro activo con la lógica de persistencia movida a una clase separada.
Patrón del asignador de datos : por ejemplo, asignaciones clásicas de SQLAlchemy. La aplicación define clases adicionales para tablas de base de datos y asignadores de datos desde el modelo a la tabla (s). Por lo tanto, la instancia del modelo se puede asignar a las tablas de varias maneras, por ejemplo, para admitir esquemas heredados. No piense que SQLAlchemy proporciona asignadores a almacenamiento que no es de SQL.
Es posible que desee echar un buen vistazo al proyecto DictShield de James Dennis
"DictShield es un sistema de modelado independiente de bases de datos. Proporciona una forma de modelar, validar y remodelar datos fácilmente. Todo sin requerir ninguna base de datos en particular".
Fuera de mi cabeza:
Defino dos dominios de ejemplo, User
y Animal
, un almacén de clase de almacenamiento base y dos clases de almacenamiento especializadas, UserStore
y AnimalStore
. El uso del administrador de contexto cierra la conexión de la base de datos (por simplicidad yo uso sqlite en este ejemplo):
import sqlite3
def get_connection():
return sqlite3.connect(''test.sqlite'')
class StoreException(Exception):
def __init__(self, message, *errors):
Exception.__init__(self, message)
self.errors = errors
# domains
class User():
def __init__(self, name):
self.name = name
class Animal():
def __init__(self, name):
self.name = name
# base store class
class Store():
def __init__(self):
try:
self.conn = get_connection()
except Exception as e:
raise StoreException(*e.args, **e.kwargs)
self._complete = False
def __enter__(self):
return self
def __exit__(self, type_, value, traceback):
# can test for type and handle different situations
self.close()
def complete(self):
self._complete = True
def close(self):
if self.conn:
try:
if self._complete:
self.conn.commit()
else:
self.conn.rollback()
except Exception as e:
raise StoreException(*e.args)
finally:
try:
self.conn.close()
except Exception as e:
raise StoreException(*e.args)
# store for User obects
class UserStore(Store):
def add_user(self, user):
try:
c = self.conn.cursor()
# this needs an appropriate table
c.execute(''INSERT INTO user (name) VALUES(?)'', (user.name,))
except Exception as e:
raise StoreException(''error storing user'')
# store for Animal obects
class AnimalStore(Store):
def add_animal(self, animal):
try:
c = self.conn.cursor()
# this needs an appropriate table
c.execute(''INSERT INTO animal (name) VALUES(?)'', (animal.name,))
except Exception as e:
raise StoreException(''error storing animal'')
# do something
try:
with UserStore() as user_store:
user_store.add_user(User(''John''))
user_store.complete()
with AnimalStore() as animal_store:
animal_store.add_animal(Animal(''Dog''))
animal_store.add_animal(Animal(''Pig''))
animal_store.add_animal(Animal(''Cat''))
animal_store.add_animal(Animal(''Wolf''))
animal_store.complete()
except StoreException as e:
# exception handling here
print(e)