1

I read a bit about DDD since at work I came upon a web application, supposedly written according to DDD principles.

Some things which I see however in this application look to me totally absurd. As I understood, the main point of DDD is about "ubiquitous language" shared by analysts and developers alike, such that the software artefacts roughly correspond to the notions/nouns/verbs/.. in the "ubiquitous language". This should allow for a more rapid translation of analysts requirements into functioning software, since, again roughly speaking, one has to think less about how to model analysts requirements, and maintenance is easier.

However what I see in the web application I came upon is that, for example, what would otherwise be modelled by a simple enumeration, such as

public enum Car{
  AUDI,
  BMW,
  VOLVO
}

is modelled in the code by three different classes Audi, Bmw, Volvo. Likewise calls to essentially the same service, e.g. CarService.buy(Car car), with different parameters values, are made by defining a separate class for each of these service calls, AudiService.buy(), BmwService.buy(), VolvoService.buy().

Is this a typical way to write DDD applications?

7
  • 1
    How are those classes implemented? Are they just "empty" classes dervied from an abstract base, or are there real differences that would otherwise need a scattering of switch, if etc. E.g. does each manufacturer/vendor have unique finance logic on how the car is actually paid for (allthough even then it seems a bit much, and maybe the correct abstraction is the finance policy itself, not the car manufacturer)? But generally, if they are getting seperate classes, there must be at least a conceptual difference, not just a requirement saying "for now we sell audio, bmw and volvo only". – Fire Lancer Sep 4 '17 at 10:34
  • 1
    In most of these classes (90%, maybe more) there is no custom logic. What I mean is that there are many such enumerations, and most class hierarchies coming from such enumeartions do not have a single class with custom logic. It just looks like someone said "ok, since it is a DDD application, let's model with class hierarchies what would be otherwise modelled with enumerations". – John Donn Sep 4 '17 at 10:40
  • 1
    If there's no car-specific and service-specific behaviors implemented in these classes, and especially if there's a lot of repeated code and type-casting, then it might simply be bad design (nothing to do with DDD). But without more info we can't really say. For example, what's the difference between AudiService.buy() and say BmwService.buy(), and how it is determined which one gets called? – Filip Milovanović Sep 4 '17 at 10:53
  • 1
    No, wrt type casting everything is OK, and there is an evident effort not to duplicate logic. "What's the difference between AudiService.buy() and say BmwService.buy()" they all extend the same abstract service, just the constructor is different (because AudiService uses AudiFooBar and BmwService uses BmwFooBar, where FooBar is yet another "generic" artefact). – John Donn Sep 4 '17 at 11:47
  • 3
    You need to provide a bit more detail if you want an answer to your question. There's no way we can evaluate whether this class design is sensible without actually seeing the classes. Anecdotally, what I see in inexperienced developers is a tendency to slavishly follow the rules of a particular methodology without fully understanding why those rules exist and giving themselves permission to break those rules when it makes sense to do so. – Robert Harvey Sep 4 '17 at 13:10
3

Is this a typical way to write DDD applications?

No, but maybe.

You are absolutely right that we don't normally create a class, or a class system, for every value of a property. In most implementations, something like a car make would be treated as an opaque label, with no distinction in behavior based on the value of the label.

So I would normally expect an implementation like:

class Make {
    public final String name;

    boolean IsEqual(Make that) {
        return this.name.equals(that.name);
    }
}

You might use a flyweight, rather than a wrapper around a primitive, for cases where the range of values is well understood and unlikely to change on short notice.

But... part of the point is to be tailoring the domain model to the needs of the specific business. The fact that Make is an opaque value in my domain doesn't mean that it is an opaque value in all domains.

Separate classes for Audi, BMW, and Volvo would certain make sense in a case where each of those Makes was tracking different state, or had different rules about how the data might change over time.

It just looks like someone said "ok, since it is a DDD application, let's model with class hierarchies what would be otherwise modelled with enumerations".

final class BMW extends Make {
    BMW () {
        super("BMW");
    }
}

It's not wrong, but it looks weird; I wouldn't expect to see this sort of thing in the domain model without some additional evidence of its necessity.

A place where you might see something like this is one where you are leaning on the type system to ensure the correctness of multiple interacting parts.

class ExchangeService<M extends Model<M>> {
    void swap(M lhs, M rhs);
}

class BMW extends Model<BMW> {...}
class Audi extends Model<Audi> {...}

void example(ExchangeService<BMW> exchange ) {
    // COMPILE TIME ERROR
    exchange.swap(new BMW(), new Audi());
}
0

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.