Create a reusable component with ZCA and SQLAlchemy

195 Views Asked by At

Actually i'm designing a large application for desktop with python 3.4. I choosed the Port and Adapter architecture as know as Hexagonal architecture. The main purpose it to work with reusable component.

To organize the code and put some rules we will use the Zope Component Architecture (ZCA)

So to do some POC, i'm creating the database component. But the fact to work with an ORM block me. I mean i designed some database component which looked like :

-IDatabase -IDatabaseConfig -IEntity -IKey -IReader -IWriter ... and the implementation.

SQLAlchemy management lot of thing and i don't know how to make my component reusable.

i found this code :

#Reflect each database table we need to use, using metadata
class Customer(Base):
    __table__ = Table('Customers', metadata, autoload=True)
    orders = relationship("Order", backref="customer")

class Shipper(Base):
    __table__ = Table('Shippers', metadata, autoload=True)
    orders = relationship("Order", backref="shipper")

class Product(Base):
    __table__ = Table('Products', metadata, autoload=True)
    supplier = relationship('Supplier', backref='products')
    category = relationship('Category', backref='products') 

But this code is realy hardly coupled with SQLAlchemy i guess. So what is the approach i should use with my architecture?

As the Entities must be the center of the application (Domain layer) it will have a problem with that solution if i need to change my database component and don't use SQLAlchemy ?

i'm open to all suggestions.

1

There are 1 best solutions below

0
On

I use the ORM as entity objects and put Adapters over it:

somewhere in a interfaces.py the API is defined:

from zope.interface import Interface

class IEntity(Interface):

    def pi_ing():
        '''makes the entity go "pi-ing" '''

somewhere the database model is defined:

class EntityA(Base):       
    # .. table and relationship definitions are here

somewhere else the API is implemented:

from zope.interface import implementer
from zope.component import adapter, getGlobalSiteManager

class EntityAPI(object):

    def __init__(self, instance):
        self.instance = instance

    def pi_ing(self):
        # make "pi_ing"


@implementer(IEntityAProtocol)
@adapter(int)
def get_entity_a_by_id(id):
    return EntityAPI(session().query(EntityA).get(id))

getGlobalSiteManager().registerAdapter(get_entity_a_by_id)

Now everything is in place. Somewhere in the business logic of the code when you get the id of an entity_a you can just do:

from interfaces import IEntityAProtocol

def stuff_which_goes_splat():        
    # ...
    entity_id = got_from_somewhere()
    IEntityProtocol(entity_id).pi_ing()

Now you have a complete separate implementation of interface, database entity and API logic. The nice thing is, you don't have to adapt only such primitive stuff like an int ID of an object, you can adapt anything, as long as you can implement a direct transformation from your adaptee to the database entity.

caveat: of course, relative to the point in execution time, the getGlobalSiteManager().registerAdapter(get_entity_a_by_id) must have been called already.