The quote from the book is concerned more about code that looks like this:
var data = {
some: {
huge: {
deeply: {
nested: {
object: {
}
}
}
}
}
};
controller1.foo(data);
controller2.foo(data);
/* which calls: */ service1.bar(data);
somethingElse.baz(data);
eventQueue.push(new WhateverAppEvent(data));
fooRepository.add(data.foos);
All of these objects are directly coupled to data
and everything it contains. Sure, passing data
as constructor or method arguments seems to follow dependency injection and inversion, but the same exact object, which is large and complex, gets passed around to multiple layers of the application. This indirectly couples these different layers together. Each layer must know the structure of data
, and since they use the same data
object, changes to one of its members could affect the other objects that use data
. If somethingElse.baz(data)
modifies data.foos
, this could impact the call to fooRepository.add(data.foos)
in a way that is difficult to identify.
The reason why Redux and similar frameworks do not fall into this trap is because they use events to communicate between components. The big, huge data structure is not passed around to everyone at every layer. The central state management system has the Giant Data Object. Individual components in your application have a subset of the Giant Data Object, or snapshots of it. Modifications to data are achieved through events. These events are used by the other components to only update the small subset of data that component cares about. This dramatically reduces the knowledge required by each component, which makes state changes easier to manage. When something goes wrong, it limits the number of places the defect could occur, when compared to every component receiving the same Giant Data Object.