I’m looking for advice where the business logic and data layers in a Flask/SQLAlchemy-based app all use essentially the same object class.
As an example, my app includes a task scheduler.
Tasks are received (from an external api) as “candidate tasks”. These are prioritised and timing constraints are added in accordance with various business rules. The user arbitrates interactively to resolve conflicts, which may require tasks to be abandoned or postponed. Nothing is persisted at this stage.
Once the schedule is finalised the tasks become “scheduled tasks” and are then persisted to the database using SQLAlchemy.
“candidate tasks” and “scheduled tasks” have near-identical attributes (essentially the only differences in the classes are the addition of DB-specific attributes to the “scheduled tasks” such as primary key, relationship definitions and table name).
It seems to me I have 3 options for defining the objects:
I define a single “task” object in the models.py module (the data layer) which includes all the attributes needed for that layer. This would include all the attributes needed for scheduled tasks, which would be a strict superset of the attributes needed for candidate tasks in the business logic layer. The advantages of this are that I only have one object class to maintain and that evolution of candidate tasks to scheduled tasks is simple. The downside is that the business logic layer is tightly bound to the data layer, which seems wrong, although I’m fairly new to ORM and maybe having problems recognising that SQLAlchemy models are a kind of bridge between the business logic and data layers.
I could define separate classes for candidate and scheduled tasks. The former would be defined in a module in the business logic layer, the latter in the models.py module in the data layer. This decouples the layers, but at the cost of requiring a translation function to copy/convert from one to the other. It also increases maintenance.
I could create an abstract base class, held in some central core module of the app, that defines all the attributes common to candidate and scheduled tasks. I then have separate candidate and scheduled task classes, as in Option 2, except that these inherit from the ABC. This seems to give me the best of both worlds and is my current preferred option.
Tasks is just one example of this issue in my app - numerous objects have similar requirements- so before I go firm on Option 3 I just wanted to confirm if this is a best-practice design pattern.
Thanks