PurelyFunctional.tv Newsletter 457: Take a stance
Sign up for weekly Clojure tips, software design, and a Clojure coding challenge.
Design Idea 💡
Take a stance
This essay continues the series about Domain Modeling.
"But what if my model isn't flexible enough? I can't predict the future."
I hear fears like this whenever I suggest defining the domain model. People often prefer the messy tangle of if statements to a clear and orderly model.
Let's address this fear from several angles since there are likely different causes behind it.
1. No need to worry: Your domain model is certainly wrong already!
You can't know how wrong it is until you write it down. Are you afraid of learning how bad it is? Follow that fear since that's the first step in correcting it.
2. Nobody gets the model perfect.
What even is perfect? We want a model that has a better fit than the current model. Better fit translates into fewer corner cases, less code, and ultimately, more development speed and less maintenance cost.
3. You can change the model later.
Don't worry about getting it wrong. It's just code and data. If you find a better model later, you can migrate to the new model. Yes, that gets more expensive as you store more data and handle more use cases. But these changes can be done incrementally, and they pay off.
4. Build flexibility into your model.
Can you predict specific areas where you need flexibility? Build them in! There are many practices for domain model evolution. But it's always better to think through where you are uncertain of future needs instead of throwing up your hands. Follow your fear as a guide to areas that need more exploration.
For instance, you might worry that you will paint yourself into a corner if you model the toppings of your pizzas since they will change over time-sometimes daily. However, that means you should use a model that allows ingredients to change quickly and easily. That means storing the ingredients in a table in the database instead of hard-coding them.
Domain models are not entirely rigid. They define where and how they are flexible.
5. Push decisions into a higher layer.
If you're uncertain about decisions, you can always choose the most general thing in this domain. Then you can decide in the next layer where it is easier to change.
For instance, if you want to change the sizes of your business's pizzas later, choose a general approach: Use a diameter in inches. You can then add a layer that translates small, medium, and large pizzas into 10-, 14-, and 18-inch pizzas. That higher layer is easier to change.
Good domain models are more general than we need at the moment. They last a long time because we can reuse them in novel situations without change.
7. Have the conversation.
The domain model defines the boundary between pizzas you can build with it and pizzas you cannot. The model's constraints are often helpful since they keep us from representing impossible situations. But the constraints can also feel confining when they limit our imaginations. What if we want a square pizza?
These feelings of confinement tell us that we need to have a conversation with the business stakeholders and customers. There is a significant engineering cost in allowing for arbitrary shapes of pizza. Have that discussion with your stakeholders. They probably don't want anything but round pizzas. Or maybe "arbitrary shape" is too general, and they only really want circles, squares, and triangles. Either way, it's a business decision, and you should talk to them about it.
Conclusion: Take a stance.
Building a domain model requires you to take a stance on how to interpret the data you receive from the world. The model constrains how it is stored and what you can do with it. If your software runs your business, the domain model defines what your business can and can't do. It distinguishes your business from competitors. If you get it right, it can be a serious competitive advantage. But if you get it wrong, it could slow down your development speed, decrease profits, and make customers unhappy. A lot is riding on it.
Since you certainly will get it wrong in any long time horizon, it's essential to be able to change it. And that's what I want to explore in the next issue.
Book update 📘
Good news: Grokking Simplicity is going into another printing! That gave me the opportunity to correct some errors my readers and I have found in the book. The book is only getting better!
You can order the book on Amazon. Please consider leaving a rating and/or review. Reviews are a primary signal that Amazon uses to promote the book. And they help others learn whether the book is for them.
You can order the print and/or eBook versions on Manning.com (use TSSIMPLICITY for 50% off).
Pandemic update 😷
I know a lot of people are going through tougher times than I am. If you, for any reason, can't afford my courses, and you think the courses will help you, please hit reply and I will set you up. It's a small gesture I can make, but it might help.
I don't want to shame you or anybody that we should be using this time to work on our skills. The number one priority is your health and safety. I know I haven't been able to work very much, let alone learn some new skill. But if learning Clojure is important to you, and you can't afford it, just hit reply and I'll set you up. Keeping busy can keep us sane.
Stay healthy. Wash your hands. Wear a mask. Get vaccinated if you can. Take care of loved ones.
Clojure Challenge 🤔
Last issue's challenge
NOTE: I put the wrong link to the gist in last week's issue. It's never too late to add your solution.
This week's challenge
Write a function that determines if a chess piece, on an empty board, can move from one space to another in one move.
(can-move? :pawn "A2" "A3") ;=> true (can-move? :queen "H1" "A8") ;=> true (can-move? :knight "A4" "A5") ;=> false ;; (that's not how knights move) (can-move? :king "A8" "A9") ;=> false ;; (that's off the board)
- This page has a nice graphic of all of the chess pieces and their moves.
- Assume that pawns are moving from the low to the high numbers.
- You can ignore en passant, pawn's capture, castling, and pawn's two-square move on the second rank.
Thanks to this site for the problem idea, where it is rated Very Hard in Java. The problem has been modified.
Please submit your solutions as comments on this gist.