2

I have a database that stores client data. All data is in one table. For each client, there is one row per day. Clients have different kinds of contracts, their data format remains the same, however, the underlying dynamics are different.

I am creating an application to simulate those dynamics and I am struggling finding a good object structure to implement this. I came up with two approaches:

Approach 1

I would start with a superclass for all types of contract. It would handle the access to the database and fundamental properties, such as the contract number, as well as fundamental methods (find first date, total value, etc)

Then I would subclass this for the specific types to add specific methods (e.g. calculate optimal index value, the algorithm of which would be depending on contract type).

However, the type of contract is determined by a value in the database. Therefore, if the contract object loads its own data from the database, it would then need to change its own class depending on the type. I don't think is good design.

Approach 2

Alternatively, I could create a contract_manager class to hold a list of all contracts, retrieve the data from the database, and generate and fill contract objects.

In that scenario however I fear that the interface to remember would be more complicated, and database access could not be hidden inside the object. This would be desirable, since these classes will be part of an analytics and prototyping library for a team of data analysts.

What would be the correct way of phrasing this problem? What would be the best approach to handle this?

Christophe
  • 74,672
  • 10
  • 115
  • 187
zuiqo
  • 927
  • 8
  • 15

2 Answers2

6

Clients have different kinds of contracts, their data format remains the same, however, the underlying dynamics are different.

According to this statement, you implement the single table inheritance pattern to map contract classes with a contract table in the database.

I would start with a superclass for any type of contract, which handles the access to the database and fundamental properties, such as the contract number, as well as fundamental methods (find first date, total value, etc)

It is of course fine to have a superclass to define the mandatory interface of all contracts, and to implement the shared behavior. However, when you create a contrat, you need to know its concrete type. You have two approaches to overcome this problem:

  • Use composition over inheritance: make Contract a class instead of a superclass, and make it refer to a ContractDynamic class, which would then be specialized according to the behavior. This way you could choose dynamically when reading the contract data from the database which ContractDynamic class to instantiate (similar to proxy design pattern)
  • Prefer Domain Driven Design: Use a Factory to create new data and a Repository to retrieve the contract from the database and instantiate the correct class depending on the row data that was read.

The second approach is much better. It ensures a higher separation of concerns, by decoupling the domain objects from the persistence layer

Christophe
  • 74,672
  • 10
  • 115
  • 187
  • Thank you for this answer. Could you please clarify a bit how the DDD-Implementation would look like? I would create a repository that queries the database, but would it also create the contract-objects? What is the role of the factory in this case? – zuiqo Jan 04 '18 at 10:25
  • Also, in DDD, would the repository be the only access to the database? – zuiqo Jan 04 '18 at 10:28
  • The repository is in principle only there to load aggregates from the database. They are [sometimes widened to include all the relevant database operations](https://lostechies.com/jimmybogard/2009/09/03/ddd-repository-implementation-patterns/), but that's not their original intent (at least according to Evan's book). DDD is about the domain but says nothing about the architecture of persistence and data access layer. You can very well use the [active record](https://martinfowler.com/eaaCatalog/activeRecord.html) approach you're currently envisaging for Create/update/delete. – Christophe Jan 04 '18 at 11:41
  • I'm trying to adopt what you suggest: A repository that holds the database connection, a contract class that holds the data, and multiple specific_contract classes that add functionality. The main script retrieves a list of contracts through the repository, then hands a specific contract id back to the repository, which will then retrieve the data from the DB, interpret the contract_type, and create a specific_contract object, and return it. Is that in line with your suggestion? Thanks again for your effort! – zuiqo Jan 05 '18 at 10:24
  • @phi yes, that should be fine ! – Christophe Jan 05 '18 at 10:28
  • I implemented it in the described way. Now my object needs to retrieve more data from the database. The easiest way would be to also give the object itself database access , but that seems hacky. Alternatively, I give each contract a pointer to the repository, which seems even worse. Or I implement another method in the repository, which gets a contract as argument, fills it, and returns it back. That would seem like very tight coupling. Do you perhaps have a suggestion for me? Thank you very much. – zuiqo Jan 08 '18 at 12:10
  • This is a topic that can become very complex and broad as your application grows. It starts with additional loads, continues with transactional capacity (several related objects are either saved all together or the change is reversed), then the pursuit of identify (if one shared object could be loaded and then modified in several places at the same time), etc... . I recommend to invest in Martin Fowler's ["Patterns of Enterprise Application Architecture"](https://martinfowler.com/books/eaa.html) book : it gives the big picture and analysis each pattern in detail, with code examples. – Christophe Jan 08 '18 at 18:08
2

Decouple the contract interfaces from the underlying storage

Don't expose any of the table data directly. Instead, make a (non-public/internal) implementation object that contains the fields for all the data items in the table (POJO/POCO).

For each use case (kind of contract), write an interface that suits the type of contract and the operations you want to do with it ideally.

Finally, write (also non-public/internal) lightweight implementations for the contract interfaces that requires the POCO object as constructor parameter. Implement the methods of the contract interfaces using the POCO as storage. Most likely this will be composites, with shared functionality between different contracts implemented only once.

Make sure your client code only knows the interfaces, not the actual implementations.

Finally, have a factory that will output the proper interface given the POCO/POJO.

This gives you the following benefits:

  • shared 'data' object that always maps perfectly to the table and makes IO trivial
  • nice interfaces that make working with the different contracts simple and easy
  • code reuse on the private implementation level
  • freedom to change the implementations of the different contracts without changing the client code (as long as the interfaces stay stable)
  • easy testability
Wilbert
  • 1,703
  • 1
  • 12
  • 16