In Robert Martin's (Uncle Bob) book "Clean Code", he provides a great argument supporting data classes. He argues that "Data Structure" objects and "Data Transfer Objects" can be a good. They have data only and no functions.
Objects: hide their data (be private) and have functions to operate on that data.
Data Structures: show their data (be public) and have no functions.
The two concepts are opposites:
Procedural code (code using data structures)
Makes it easy to add new functions without changing the existing data structures.
Makes it hard to add new data structures because all the functions must change.
OO code (code using object oriented)
Makes it hard to add new functions because all the existing classes must change.
Makes it easy to add new classes without changing existing functions.
The Law of Demeter(LoD)
A method M of class C should only have access to C and M parameters. It should not access parameter1.getSubItem().getSubSubItem(). It should not know about the inner workings of its parameter classes.
Data Transfer Objects
This is a form of a data structure which is a class with public variables and no functions and sometimes called DTO. DTOs are very useful structures, especially when communicating with databases or parsing messages from sockets and so on.
Source: Clean Code | Chapter(6) | Objects and Data Structures
Uncle Bob requires that you not mix data classes and OO classes. So if your class has any logic, it becomes an OO class and if it also exposes it's internals via getters/setters, that is bad.
An interesting case of "Data Structure" classes is static inner classes. I regularly use "Data Structure" static-inner classes which are only accessible by the containing class. They are used to construct the data structures for my class. For example HashNode, ListNode, Pair, Tuple.
I would potentially even extend this static-inner-class argument to a module. There might be some "module-private" data-structure classes. They are not part of the module's public api. They are only for internal use inside the module (by the function-classes or services with a public api). But to another developer reading the code who is averse to data-structure classes, they might have trouble seeing the distinction that the class is a "module-private" class (not accessible by the module api) and just see a plain class among many classes in the repo which exposes all its internals publicly (this situation happened to me once in a PR review). So this kind of design can be slightly controversial/problematic.
I often program MVC code-bases. We have model objects or database ORM data objects. Should these be data-structures? Or should they be OO classes and have all their internals hidden? I find this is a common difficult situation hit by a lot of people. And these ORM classes commonly have both OO methods and getters/setters to access the database data. I don't have a great, definite answer to this. I don't necessarily think that all model objects should be locked down with zero getters, with a zealous fanaticism. But Tell-Don't-Ask can be a good principle to try to follow. Whatever you do, I really believe in simple, readable code. I feel like ORM classes are a special example because they are like the api to accessing the database. (Note also that the database can be thought of as a store of globals!)
What is definitely bad is when every object everywhere can freely reach into any other object freely without constraints, across a huge code-base. Particularly if there are lots of globals/singletons/globally-injected-services. This descends into a spaghetti mess. You want to reduce the scope of what-depends-on-what. If I change the structure of this class, what will break? Nothing outside the module should break if you have not changed the module public api. More importantly, when you are troubleshooting, it is hard to reason about code if an object's internals can be fiddled with all across the code-base.