Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.
03-21-2021 03:26 PM
Hi,
I'm interested in building complex Python app based on Neo4j.
As I'm also influenced by software architecture, TDD and testability, I recently found an amazing online book "Architecture Patterns with Python"
Among the architectural patterns presented, the repository pattern struck me as essential in order to write my Python app with testability in mind
The intent of the pattern is to add an abstraction between the core parts of our application and the database to increase flexibility and testability, through the dependency inversion principle.
Example from the book with SQLAlchemy
Benefits:
This is my attempt at building this pattern for my application with py2neo
:
First I defined my model:
Even though it should be clear from infrastructure definitions, I still had to implement the idea of a graph database here, by specifying 2 classes GraphNode
and GraphRelationship
.
model.py
Property = # TODO
class GraphNode:
pass
class GraphRelationship:
pass
# represents our users
class User(GraphNode):
name = Property()
Then I created an abstract repository interface, with a single match
method for now:
repo.py
from model import GraphNode, GraphRelationship
from abc import ABC, abstractmethod
class AbstractGraphRepository(ABC):
@abstractmethod
def match(object: Union[GraphNode, GraphRelationship]):
raise NotImplementedError()
And implemented a concrete class based on py2neo
py2neo_repo.py
from repo import AbstractGraphRepository
from py2neo import Graph
class Py2NeoRepository(AbstractGraphRepository):
def __init__(url: str):
self._graph = Graph(url)
def match(object: Union[GraphNode, GraphRelationship]):
# how to map my domain model to py2neo OGM ?
I'm stuck at this step above , figuring out what's the best way to define a mapping between my domain model, and py2neo's OGM.
If we take a look how they implemented this pattern in book, based on SQLAlchemy, we can see the following definitions:
orm.py
from sqlalchemy.orm import mapper, relationship
import model # dependency-inversion: the ORM depends on the model
metadata = MetaData()
order_lines = Table( #(2)
"order_lines",
metadata,
Column("id", Integer, primary_key=True, autoincrement=True),
Column("sku", String(255)),
Column("qty", Integer, nullable=False),
Column("orderid", String(255)),
)
...
def start_mappers():
# mapping is defined between the OrderLine model object and the order_lines table
lines_mapper = mapper(model.OrderLine, order_lines)
And this allows them to implement a concrete SQLAlchemyRepository
really easily:
sqlalchemy_repo.py
from model import Batch
class SqlAlchemyRepository(AbstractRepository):
def __init__(self, session):
self.session = session
def add(self, batch: model.Batch):
self.session.add(batch) # the mapping and translation from the Domain Model to the ORM is done transparently, thanks to the mapping !
def get(self, reference):
return self.session.query(model.Batch).filter_by(reference=reference).one()
def list(self):
return self.session.query(model.Batch).all()
how to implement the same pattern with py2neo
or neomodel
as concrete repositories ?
how to translate from a domain model to the OGM while keeping the overall code simple and efficient by design ?
Thank you in advance !
pinging @technige and and maybe Robin Edwards from neomodel
03-28-2021 01:45 PM
03-30-2021 09:55 AM
Hey Simon,
thank you for your answer ! I'm glad to see other people interested by testability.
The video details unit testing with the help of an in-memory database (a fake), which is also exactly what I am attempting to do with Neo4j Python here.
The question is how to design a good enough interface to honor the repository pattern and be permissive enough to reflect Neo4j's capabilities.
All the sessions of the conference are now available online