1. Start with code centered around pure functions. This is a really good default for your project. It gives you flexibility, completely DRY code, and an easy way to think about concurrency.
2. Store large datasets or shallow objects inside maps. They can be efficient and work really well with functions, especially in languages like Python and Clojure. They're even more useful when, for example, they have a literal dot notation (Javascript) for keys.
3. Sometimes, though, it's just easier to structure data inside of objects. Having a map of maps of maps is fine, but projects can quickly become confusing--at least in my experience. The problem is especially bad if multiple people are coding around maps without any enforced structure or validation on creation. In these cases, objects can make things simple and (often) more unified. When I use objects, though, it's good to avoid mutable state. I think of an object as a handy, unified interface to a map, but with the following extras in a unified way:
- A constructor, potentially with sensible defaults
- Validations and sensible fallbacks
- Data-specific error messages
- Methods that are closely related to the data it
holds (and always return the object itself, so
chaining is simple)
In my perspective, building code using functions and organizing it around objects is a nice middle point. It's optimized for both productivity and order, especially if you're careful to avoid state.