Software architecture (Domain Modelling)

Hi all,
First to set the stage. Say we have an app that represents a Shop that sells Books amongst other things. Books have differential pricing. So we have chosen to design DB schema as two tables. Books and BookValues with 1 to many relationship. With Ecto we will get a schema struct which is something like

%Book{
  id: UUID
  prices: [%BookValue{price: 10.0}, %BookValue{price: 12.30}]
}

One way to expose this to client side browser is to simply use this struct as the underlying and generate JSON which will look like this

{
  id: "...",
  prices: [{price: 10.0}, {price: 12.30}]
}

But I feel this should not be the case. I feel we should have proper modelling and segregation of concerns.

So we should have a domain struct that models actual real life books and the JSON payload would make more sense to me (personally)

[
{id: "...", price: 10.0}, 
{id: "...", price: 12.30}
]

Argument AGAINST this is

  • JSON payload would be heavier
  • Feels like there will be a lot of needless conversion between schema struct and domain struct

Argument FOR this is

  • We decoupled concerns cleanly (presentation, biz logic, storage)

Are there any other points to support FOR/AGAINST this?

This is one of those big ol’ “it depends” questions (emphasis on “big ol’” since technically the answer to any question is “it depends”).

While I feel you’d just like me to take your example at face value, I think in this case more details are required. I see a book has multiple prices, but what are these prices based on? Are they categorical (like good, fair, poor)? Does two or more books having the same price have any other meaning other than they just cost the same?

I am generally not concerned about heavier JSON payloads but the above questions still stands. If prices are simply just a number, I wouldn’t even bother modelling them—just return prices: [10.0, 12.30] as a list of primitives. If prices have more to them than that I think you have to answer the question: “Do books without prices make sense?” If not, always return a list of prices; if so, don’t.

Also, I’m realizing I’m assuming you’re talking about a REST API here? I’m not actually sure. I’m so entrenched in LiveView at this point that I don’t really think about JSON payloads anymore and have been reframing your question a bit in terms of schema vs embedded_scehma, but your question stuck a chord so I thought I’d try and help!

1 Like

Thank you for taking time to discuss about this. Yes prices are simply that just floats for now. Indeed in ShopWeb box I should have included an embedded_schema struct. But in this case it would likely be the same as domain struct.
But potentially there could be a case where we want to somehow return a JSON payload that requires massaging of the domain struct and that’s where embedded_struct will come in.

Well I think you have to still answer if there is any categorical meaning to prices or if they are simply prices. Maybe that is what you are saying? But, to say something very general and boring about programming: I would never take “potentially” into account—Always do the simplest thing you can get away with right now. You can’t predict the future so don’t prepare for it (note: I’m scoping this claim purely to software dev :sweat_smile:). Of course, if this is just a hobby project and you’re looking to learn it’s a different story, but in that care I still have my litany of questions!

But because they are just floats right now (though I would urge you to use either Decimal or integer stored as cents) I would just make prices a list of floats (primitives). This is so dead simple that if-and-when your needs change it will be much easier to refactor than if you prematurely try and model prices upfront.

1 Like

The ... here seem to be hiding something important: do both of the objects in this format have the same ID? That would make handling them as two separate “things” much more complicated.


A technique I’ve found useful for thinking through these problems is to focus on the desired operations and produce a data structure that makes those easy to write.

For this case, what happens when a user wants to buy a particular Book? Do they pick a price as well, or does the site match them somehow (by lowest price available, etc)?

If the site matches them automatically, specifying the ID for Book would be enough.

If they pick a price (think a marketplace like Discogs that shows condition, etc) then perhaps BookValue’s ID should be surfaced.

3 Likes

YAGNI :smile:

1 Like

Oh sorry for the confusion its just be being lazy. They will be distinct UUID strings.

two short unrequested comments that don’t answer your question but may help.

As mentioned by @sodapopcan, never use floats for money. Binary floating point is an imprecise representation of decimal numbers, and you want your money amounts to be correct to the last cent. Always use arbitrary precision libraries like Decimal.

Keep the names in your code simple and close to the actual model: don’t name it BookValue, use BookPrice instead.

2 Likes