2

The task is to make a migrator from Old DB to New DB using OOP Single Responsibility Principle.

enter image description here

My problem is how can I make this without making the controller a God Class or breaking the single responsibility.

I've thought of two methods of implementing this

  1. Iterate through each property row and cascading to Subdivisions and Owners in the controller and call a Row Migrator for each entity type

    • Been told that is giving too much responsibility to the controller and would make it a God Class
  2. Add the iterations inside each Entity (E.g. Property iterates Subdivisions, Subdivisions iterate Owners)

    • Been told that it is breaking the single responsibility principle

Right now it feels that anything I think of it either over-complicates or breaks rules. Maybe I've understood the principles wrong or there is a third way.

Update 1

Normally I would do this with a script but the requirements are to use an API as the new DB is on a different platform, and the addAddress() and addOwner() logic will be in an API Component.

5
  • sorry about the missing "r" in the diagram, seems my dia export went wrong Jul 29, 2015 at 6:46
  • 1
    You're trying to create an OOP migration script!?
    – Nathan
    Jul 29, 2015 at 6:54
  • I simplified the requirements, It will use an API Component to migrate the data, and the storage will be on different technologies (e.g. MySQL to MongoDB) Jul 29, 2015 at 7:49
  • Slightly unhelpful comment: In my experience this type of problems is not that nice to map to an object oriented design. If you are willing to learn something new, I've had great luck in using functional programming (with languages like F# etc) to create nice, maintainable, short programs for these kinds of tasks. Generally, when I compare a good OO-design and a good FP-design for this problem the FP-design often clocks in to much less than half the code of the OO-design, often less. Especially if you use stuff like type providers etc.
    – wasatz
    Jul 29, 2015 at 13:16
  • @wasatz I know FP-design is good, simple and fast to code and execute, even with a bad oo-desgn this would have been simple. But it's not my decision to make. Jul 29, 2015 at 16:17

2 Answers 2

4

OOP is not always the best choice for ETL tasks, thus I would not stick to "pure" OOP here. And as long as your model is as simple as shown in your example with just three tables to migrate, I would avoid to overdesign this - having simply one Controller which orchestrates the migration of just three tables, and which uses different "Row Migrator" classes, as you wrote, does not look to me as if the Controller gets "too many responsibilities".

Nevertheless there are some options to distribute the responsibilities. For an ETL program, I would suggest to split the responsibilities between the "E(xtract)", "T(ransform)" and "L(oad)" parts of your migration - the "Extractor" class is the only one which connects to the old DB and provides the data to migrate, the "Load" class the only one which connects to the new database / uses the API, and the "Transform" class which does the transformation work, but does not know anything about the source or the destination of your data. Moreover, when your model is much bigger in reality as shown in your example, you might consider to split the "Transform" class into smaller "sub-transformers".

If your second option makes sense IMHO depends on if you are going to use the entity classes exclusively in the migration context, or if you want to keep them reusable and maintainable for different contexts. For the latter, I would not add any business logic there which is only useful for this very special use case "migration".

4

You don't need a "controller", you need a "service."

A Controller controls the flow of the application based on user input and other events that occur in lower layers. A Service just naively goes about its business doing what it was told.

The migration service object would have the intelligence for pulling data from the old schema and persisting it to the new schema via a web service if necessary, or an entirely different API. This service should have the minimum amount of dependencies to get the job done:

  • Domain model classes for each database
  • Data access classes for each database
  • Methods or other classes that map one domain model to another

The PropertyMigrationService has the following responsibilities

  • Knowing where to pull data from
  • Knowing where to save data to
  • Having access to the mapping objects to map one domain model to another

It would need:

  • OldDataAccess - A class for CRUD operations on the old DB
    • Property - The domain model for the "property" in the old DB
    • Subdivision - Domain model for subdivisions in the old DB
    • Owner - Domain model for owners in the old DB
  • NewDataAccess - A class for CRUD operations on the new DB/web service
    • Address - Domain model for properties in the new DB
    • Owner - Domain model for owners in the new DB
  • PropertyToAddressMapper - Mapping Property objects in the old DB to Address objects for the new DB
  • SubdivisionToAddressMapper - Mapping Subdivision objects in the old DB to Address objects for the new DB
  • OwnerToOwnerMapper - Map owner objects from one DB to another

You'll end up with this object hierarchy:

  • PropertyMigrationService
    • OldDataAccess
    • NewDataAccess
    • PropertyToAddressMapper
    • SubdivisionToAddressMapper
    • OwnerToOwnerMapper

The migration service could have a single public method called void migrate() that performs the migration.

Having dealt with legacy schemas, you never know what manner of madness and chaos you'll encounter. Architecting the solution in this manner, in conjunction with having your data access classes implement an interface, and have the PropertyMigrationService use those objects through their interface, you can make this whole migration process unit-testable.

Now you can throw a bunch of tests at this for known error conditions and really make this thing bullet proof (well, bullet resistant).

After this is wrapped in a service object, you have some flexibility for when and where this gets invoked:

  • From a "controller" in a web application
  • From a script kicked off as a cron job in Linux or scheduled task in Windows

The migration process becomes both proactive (user clicks a button on the web page and does the migration) or passive (a schedule task executes at midnight and does these records in batches of, say, 1,000).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.