Question

I have an application where the requirements are to show a list of (just for example) car manufacturers. When you click on one of the manufacturers, a list of car models show up. You can then click on the car model to show other data related to the model.

My problem is that one of the manufacturers is to be treated as a car model. There will be only one car model under this manufacturer. The below is an example of the current database structure. As you can see, there is a MegaCar manufacturer and there is only one model under this manufacturer (with the same name). When you click on the MegaCar manufacturer, it should directly load the MegaCar model instead of showing the list of models.

manufacturers

id name
1 Ford
2 Toyota
3 MegaCar

models

id manufacturer_id name
1 1 Focus
2 1 Mustang
3 2 Camry
4 2 Corolla
5 3 MegaCar

I'm trying to decide on a database design and application design to represent this relationship. Basically when someone clicks on the manufacturer MegaCar, I will need to load the model page for MegaCar (where models.id=5), not the manufacturer page (where manufacturers.id=3). I will need to update the database schema for this. My current line of thinking is to add a isModel flag to the manufacturers table and my code would look something like this:

object = getSelectedManufacturer();
if (object.isModel) {
    singleModel = query('
        SELECT models.id FROM models 
        INNER JOIN manufacturers ON models.manufacturers_id = manufacturers.id
        WHERE manufacturers.id = [object.id]
        LIMIT 1
    ')
    renderModelPage(singleModel)
}
else {
    listOfModels = query('
        SELECT * FROM models 
        WHERE models.manufacturer_id = [object.id]
    ')
    renderManufacturerPage(listOfModels) // model list page
}

The problem I have with this is it might be confusing to other people who see my code. Also it seems wrong that the relationship between these tables is one-to-many, but I'm making an exception for one record.

I had another idea to add a nullable modelId column to the manufacturers table, but I didn't like this for the same reason as above. Also, I've seen some Q&As around here advising against using NULLs.

What would be the proper way to design this application?

Was it helpful?

Solution

My problem is that one of the manufacturers is to be treated as a car model. There will be only one car model under this manufacturer.

Ok.

When you click on the MegaCar manufacturer, it should directly load the MegaCar model instead of showing the list of models.

Stop! You are mixing up concerns here, persistence and presentation.

Both things are very different concerns each of which faces different problems that change at different times and for different reasons due to different actors. You should not couple them so tightly.

The persistence shouldn't depend on the view because changing the view is cheap and it happens oftener than we think: new features, new UX, rebranding, marketing, overhauling... The view layer is terribly volatile and the persistence cannot follow such volatility.

I'm trying to decide on database design and application design to represent this relationship.

You already have it and it's ok.

To me, object.isModel is out of place because it doesn't only couples persistence and view-specific requirements, it also puts at the same level two different abstractions models and manufacturers. To my experience, object.isModel carries the seed of the evil.

Why? Because it's prone to cause a proliferation if/else blocks all over the source code. Something that will extend up to the SQL statements too. Maybe it's not obvious yet, but it will be.

Finally, it opens a window for other developers to solve similar issues with the same solution: mix up of abstractions, coupling and code segmentation. Going that way will make the solution (as a whole) less consistent, harder to reason about and harder to maintain in the long run.

Q: What would be the proper way to design this application?

If consistency and cleanliness matter, treat these cases as any other manufacturers-models relationship. If the view layer demands direct access to the model bypassing the manufacturer; make the application layer to solve this.2

The solution of bypassing manufacturer should be implemented and encapsulated in the view layer and be configurable. This way, the whole application gains in resilience and versatility.


1: The view is only used for querying. It doesn't need to be mapped to its own entity in the source code.

2: Don't worry yet about the number of calls to DB. Implement the number of calls you need. Solve the problem first, optimize later.

OTHER TIPS

Think longer term - eventually... there will be a MegaCar 2.0 Or there will be another new car company with only the one model. Or one model companies all go out of business.

Keep it the way you have it as if there were multiple models for each manufacturer, and deal with it when you get the results of your select statement. Check number of rows returned, if 1 then forward immediately, otherwise build/show the list.

object = getSelectedManufacturer();
if (object.isModel) {
    listOfModels = query('
        SELECT * FROM models 
        WHERE models.manufacturer_id = [object.id]
    ')
    if(listOfModels.length()==1){
        renderModelPage(listOfModels)
    }else{
        renderManufacturerPage(listOfModels) 
    }
}
Licensed under: CC-BY-SA with attribution
scroll top