Let's say I have an which is loosely can be represented as:
public class AnObject{
public AnObject(String name, String value, UUID id) {
this.name = Objects.requireNonNull(name);
this.value = value;
this.id = id;
}
final String name;
String value;
UUID id;
}
Now, willing to limit null
usage, I would naturally have 2 variations of it, one for Database (with all its non null
constraints, below), and one which will be instantiated at run to be sent to some remote resource (nullabe
, above). The one to be sent to the remote resource, have the same fields, but they can be nullable.
So we have:
public class AnObjectDBRow {
final String name;
final String value;
final UUID id;
AnObjectDBRow(String name, String value, UUID id) {
this.name = Objects.requireNonNull(name);
this.value = Objects.requireNonNull(value);
this.id = Objects.requireNonNull(id);
}
}
When I receive my object from Database, I am sure to have a non-nullable version, but when I send it to a remote resource, I can omit nullable fields.
Question: given that this obviously doubles the number of classes to declare and to maintain, is that a good idea? What's the best way to represent this in java?
1 Answer 1
Since you can model your data in multiple ways, you might get more than 2 classes out of this. You have the persistent state with its invariants, but that doesn't mean there's a single way to represent the data.
This is where Data Transfer Objects or DTOs come in play. I present to you some potential ways to view your AnObjectDBRow
(or multiples of them):
public class NameValueDTO {
String nameAndValue; // Contains name + " " + value
}
public class ValuesForNameDTO {
String name;
Set<String> values; // All values for given name
}
So does this mean you need to write more classes? Yes. It also documents your code when using proper naming and comments, as well as allowing stricter typing than having classes where things "can be null depending on how we're using it".
As DTOs are light-weight classes, there's not that much to maintain. A lot of the copying of values can be handled with automatic mappers (like Dozer).
It's your choice, do you want fewer classes or a more robust codebase?
Let's use a more realistic example:
public class UserEntity {
String username;
String firstname;
String lastname;
byte[] hash;
String location; // Could be null
}
public class UserDTO {
String firstname;
String lastname;
String location;
}
Here we've left hash
and username
out of the DTO. We use them to authenticate, but this DTO is designed for human readable data so the result is a nicely encapsulated object. No usernames or password hashes here to confuse the people who use this class.
Of course the fact that location
is null must be taken into account somehow, but that doesn't mean you need a class that doesn't have location
attribute. Then you'd have to create 2 types of objects based on whether a field was null in the database, and that just wouldn't make sense.
-
Being just a singlestring, your NameValueDTO naturally decays to merely a string. You don't need the class, in other words.Robert Harvey– Robert Harvey09/16/2019 13:11:10Commented Sep 16, 2019 at 13:11
-
@RobertHarvey except there's a whole lot of difference between passing
String
and passingNameAndValueDTO
. A simplified example sure, but it does contain meta information in the classname, which makes it a little bit more than just aString
.Kayaman– Kayaman09/16/2019 13:18:31Commented Sep 16, 2019 at 13:18 -
Hmm I recognize the importance of a robust typing and codebase, but let's say I have N nullable fields for 1 object. Would I declare 2^N classes to represent possible combinations with only
non-null
fields?.. From Your answer, it follows, yesDenis– Denis09/16/2019 13:48:18Commented Sep 16, 2019 at 13:48 -
@dgan of course not. You would design your DTO classes so they support your business logic. You don't create classes blindly based on whether a field is nullable or not. You may just have a DTO that's near-identical to your entity class, if there is nothing special about it. But they are on different layers of abstractions, so there is a significant difference between those classes.Kayaman– Kayaman09/17/2019 06:17:14Commented Sep 17, 2019 at 6:17
-
@dgan edited to include a better example.Kayaman– Kayaman09/17/2019 07:23:56Commented Sep 17, 2019 at 7:23
NOT NULL
columns in the db.