0

Assume I have a lot of DTO for a same domain model like this:

class User{}
//DTO
@Value.Immutable
interface SimpleUserDTO {
        getName
        getAge
}
@Value.Immutable
interface RegisterUserDTO {
        getName
        getPassword
        getAge
        getAvatar
}
@Value.Immutable
interface ChangeUserPasswordDTO {
        getName
        getPassword
        getNewPassword
}

Does it make sense to create separate interfaces and reuse it in DTO:

interface Name{ getName }
interface Age{ getAge }
interface Password{ getPassword }

@Value.Immutable
interface SimpleUserDTO extends
        Name,
        Age
@Value.Immutable
interface RegisterUserDTO extends
        Name,
        Password,
        Age,
        Avatar,
@Value.Immutable
interface ChangeUserPasswordDTO extends
        Name,
        Password,
        NewPassword

This way, I can achieve two things:

  • Every DTO will always have the same field signature, for example: Name will always have String getName(), avoiding mistakes
  • Centralize Annotation, avoid duplication
  • Easier to distinguish different fields from differnt DTOs, so getName from ChangeUserPasswordDTO is the same as getName from RegisterUserDTO since they both extends the same Name but different from getName from BlogDTO
3

The main consideration here is the Intefrace Segregation Principle

The interface-segregation principle (ISP) states that no client should be forced to depend on methods it does not use. ISP splits interfaces that are very large into smaller and more specific ones so that clients will only have to know about the methods that are of interest to them.

For the sake of clarity, let's look at a different example (excuse the C# syntax).

public interface IManageUserService()
{
    void SaveUser();
    void DeleteUser();
}

Should these interfaces be split into an ISaveUserService and an IDeleteUserService?

The answer to that rests on whether it's possible for a class to implement one and not the other. If your business logic requires a service to implement both (or neither), then they should stay together. If it's perfectly possible for a service to only perform one of the tasks, then they should be separated.


Back to your example, we can ask the same question here:

Can there be classes that only have a Name or an Age, but not both?

I would assume so. So that means that it's indeed correct to split the interfaces.

Footnote
There is, however, an argument of reasonability. If it's technically possible to use only part of an interface, but it doesn't actually ever occur in practice; then it might be better to keep the interface together in favor of writing less boilerplate code.

In such a case, you need to consider the likelihood that you will have to split the interface in the future (if an object is developed which does only use half of the interface). If the odds are low, then it's fair to avoid unnecessarily splitting the interface. If the odds are high, you're better off doing it now instead of having to refactor it later.


Splitting the interface into sub interfaces

We may be reaching a point of trying to split atoms here. Are you going to make an interface for every property that exists? Because that's a whole lot of work, which doesn't really yield any direct benefit as far as I can see.

  1. Every DTO will always have the same field signature, for example: Name will always have String getName(), avoiding mistakes

Technically correct, but not worth the effort of creating interfaces for every property. In my opinion.

Other than intellisense completion and compile-time spellchecking, is there a logical reason to do this?

  1. Centralize Annotation, avoid duplication

This seems like the same argument as the previous bullet point.

3.1. Easier to distinguish different fields from differnt DTOs, so getName from ChangeUserPasswordDTO is the same as getName from RegisterUserDTO since they both extends the same Name

If there's no relation beween Foo.Name and Bar.Name, is there really something to gain from having Foo and Bar implement the same IName interface?

I'm not convinced that you're gaining anything that outweighs the effort of making it so. The only benefit from having an interface is ensuring a contract between several classes that implement the same interface. Why is it ever going to be relevant for FooDto and BarDto to be addressed interchangeably (i.e. as a IName type variable)?

3.2. but different from getName from BlogDTO

I'm a bit at a loss here; but that may be language-specific. Is this missing from your example code?

I thought your goal was to use the same interface for every (dto) class that has a Name property? Why would BlogDTO be different then?

4

In the context of object-oriented development neither options are correct. For a short answer however: you could try to create DTOs that include each other, in which case you wouldn't have to duplicate stuff.

Back to OO: Interfaces, and abstractions are supposed to be used over business functions and not data. Therefore creating an interface for data or using DTOs in the first place is usually a sign of non-oo design.

If you want to fix this issue just try to model functionality: change password, register, displayAvatar, things like that.

  • I's a bit skeptical about the "not data" claim. Do you have any reference for this? – Flater May 8 '18 at 11:47
  • @Flater, Interfaces for data types are a waste of time and just create an unnecessary abstraction. However, "using DTOs in the first place is usually a sign of non-oo design" just leaves me feeling "whatever!" If that's the case, then I'm all for "non-OO design". – David Arno May 8 '18 at 12:50
  • @DavidArno, Unless I'm misunderstanding, is IAuditable (i.e. an interface that tracks createdon, updatedon, createdby, updatedby for an entity) not an example of a data-interface? Because that interface has a very clear purpose and is not unnecessary nor a waste of time. – Flater May 8 '18 at 12:54
  • @Flater, what implements IAuditable? If it's just one class, that purely contains data and no logic, then what purpose does that interface serve? – David Arno May 8 '18 at 12:56
  • @DavidArno The interface is applied to entities for tracking a basic CRUD history (if a full change tracking table is not warranted). The interface allows for these fields to be automatically filled in (e.g. override in EF OnSaveChanges) for all entity types. – Flater May 8 '18 at 13:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.