0

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:

  1. 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.

  2. 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.

  3. 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

nakb
  • 119
  • 3
  • Since you mention that scheduled tasks are a superset of candidate tasks, it makes sense to separate the two both logically and in the database. If it was just a matter of changing a single column (like "task_type") then a single table would be sufficient, however if it goes beyond that then you can easily take your abstract base class and make that into a database table with the common attributes of the different types of tasks. – Jessie Sep 20 '19 at 19:30
  • Thanks. My situation though is that candidate tasks don’t need any DB representation at all (they are not persisted), just scheduled tasks - and not all (maybe not even most) candidate tasks will end up as scheduled tasks. – nakb Sep 20 '19 at 21:09

0 Answers0