Mainly, it comes down to readability and maintainability. For example, let's say that you're using a method to create a new user, and this method can accept the first name, last name, email, username, street, city, state, postal code, country, preferred language, preferred currency, and a dozen other properties. Even if you have some kind of auto-complete hinting system, you now have to decide how you're going to remember which parameter is which when you decide to add a new property, which leads to something like:
String firstName = 'First';
String lastName = 'Last';
String email = 'example@example.com';
String username = 'username';
String street = 'Street';
String city = 'City';
String state = 'State';
String postalCode = '12345';
String country = 'US';
String language = 'en';
String locale = 'en_US';
String timezone = 'UTC';
String defaultApplication = 'Home';
UserDomain.createUser(
firstName,
lastName,
email,
username,
street,
city,
state,
postalCode,
country,
language,
locale,
timezone,
defaultApplication
);
If this API ever changes, your code may break, because you can't guarantee the order is correct anymore. This increases maintenance requirements because you now need to remember a very long list of parameters.
In addition, some languages have a maximum number of parameters you can supply to a method. In the server-side language of Salesforce, the Apex Code runtime limits a method to 32 parameters. If you need more than that, you're now writing so-called "DTO" (Data Transfer Objects) classes to handle that data anyways. Further, most languages allow you to only return one return value. If the result may include multiple values (e.g. isSuccessful/errorMessage), it's also helpful to have such an object to represent this type of output.
You might liken this to the difference between a SOAP/RPC and REST endpoint. With SOAP or RPC, the parameters are always in order, and any deviation will result in a runtime error. In those situations, developers typically end up using some kind of DTO object anyways, which mitigates errors caused by revisions to the API. REST, by contrast, tends to use JSON. The great thing about JSON is the parameters are almost always wrapped up in an object anyways. The parameter values don't care about their order within the object (at least, in any sane implementation of JSON parsing).
However, if there are no properties to accept/return, I would state that this falls squarely in the YAGNI (You Ain't Gonna Need It) rule. If a method is designed to never return a value, it should be a void
(or equivalent in the language) return type. Returning anything else increases cognitive load by suggesting that some type of return value is possible. Clear, concise code should strive to not increase cognitive load unnecessarily. Similarly for the input parameters, if there's no expected input, there should be no such parameter defined.
Personally, I'm against calling a class a DTO class unless it does, in fact, cross the wire from one device to another (typically client-server, but also possibly client-client or server-server). The name of the class and its context should be enough to explain the nature of the class. I would expect a User class to have the properties of a user. It may also have methods to manipulate that data, or that code might be in a domain layer that uses this object to deal with users; that should be clear from context.
So, the pros for a "DTO" class, whether or not it is called a DTO, is that it greatly improves maintainability and readability. While I could see an argument against such objects for literally one parameter, it really comes down to a design choice. When there is nothing to transfer in a given direction, there should be no parameters, or a void return type.
public DerivedFromBaseResponse FunctionName(DerivedFromBaseRequest request)
so our handler could find and handle those methods. That whole requirement was one persons brainfart, because throwing exceptions in that interface (SOAP) was well defined. But I guess the decider was afraid of them.