2

Assume I have a value object in my application with random numbers for underlying values. These value objects will serve as attributes to my entity (aggregate root). In my specific case (just for context), I'll need to generate 2 value objects, UUID and VerificationCode, when creating a Registration aggregate root.

So my question has a few related parts.

  1. in what layer is the underlying random value generated? Is it in these value objects themselves? Is it external to value objects?
  2. at what layer would these value objects be instantiated? Is it in the aggregate or is it in the command handler/use case layer?
alaboudi
  • 285

4 Answers4

5

Where are random numbers generated in Clean/CQRS architected application

Well, let's see

enter image description here

cleancoder.com - The Clean Architecture

I'd be comfortable seeing random treated as a device from the outer layer. One that random is requested from. This makes it easy to mock and test. When dealing with random in tests that need to be fast and reliable, control your random. That will make it deterministic.

If you're app is truly generating random on it's own, rather than getting it from something, well that is something the use case would ask for from your algorithms. Still something you'll want to be able to mock to make test behavior deterministic.

There are other kinds of tests. Ones that you feed random into. You'll need to be patient with some of them because it takes a lot of random before the law of large numbers takes over and makes your tests reliable again.

in what layer is underlying random value generated. Is it in these value objects themselves? Is it external to value objects?

at what layer would these value objects be instantiated? Is it in the aggregate or is it in the command handler/use case layer

Let me answer these both together.

The value object idea is all about identity. These immutable little creations "are" their values. A value object coordinate of (1,1) can be the coordinate of (1,1) for all (1,1)s. Which is handy if you need a lot of them because all those needs can be pointed right here at this one value object.

Your value object is (UUID, VerificationCode) and if I read you right you need random to make those values. Which means this is really a construction problem. In which case I have bad news for you. Clean Architecture is an object graph. It's a diagram of what the persistent object structure should look like after construction. Neither the diagram nor the book say anything about how to build it. That's left up to you. If you want to read some good books about solving the construction problem try Mark Seemann.

Presumably though, your value objects aren't going to be persistent objects that appear in the CA object graph anyway. You just want to know what would build them. For that, I expect a use case would do the job nicely, even if your random is sourced as a device.

The story could go like this:

Use case is called, it knows it needs one of your value objects created. It's job is to orchestrate this but it doesn't have to do everything. Without knowing which random device you've plugged in it can ask that the needed random be supplied to some presenter that knows how to layout and format that random as needed by the value object you wish to hang off your entity.

There are many other ways to do it of course. This just lets the layers provide some useful separation of responsibility.

Now, if you're looking at that little diagram in the corner wondering how you'd do all that, please understand the point of that open arrow is that flow of control can go against it back out of a layer without being forced to use return.

That power lets the flow of control look like this:

enter image description here

crosp.net

That flexibility can be very handy.


As for CQRS:

enter image description here

martinfowler.com - CQRS

CQRS? With random? Er, what commands are you sending your random service? Usually we just read from random and figure out how we want to present it. We don't write to random.

candied_orange
  • 119,268
2

at what layer would these value objects be instantiated? Is it in the aggregate or is it in the command handler/use case layer

Random number generators are a lot like clocks (mutable state, possibly shared, definitely not under control), so it may make sense to think about your random stream of items like time:

If you don't consider time an input value, think about it until you do - it is an important concept -- John Carmack, 1998

If that analogy works, then you'll normally do the randomness outside of the domain model (so in the "application layer", if you are using the sort of layered design that Eric Evans described in the "blue book").

This is essentially the same answer that you would get if you were following Functional Core / Imperative Shell.

So when we pass data across a boundary, it is always in the form that is most convenient for the inner circle. -- Robert Martin

Following this guideline, we'd expect the information to be expressed as a domain value prior to being passed across the boundary (so the interface of the aggregate would normally be expressed in values, not in general purpose data types).

The fact that the value happens to be randomly generated, rather than deterministically (from a user input, or a previously stored value) is an implementation detail that normally won't concern the logic of your domain model.


in what layer is underlying random value generated. Is it in these value objects themselves? Is it external to value objects?

As a general rule, your value objects will normally implement an initializer (aka a "constructor") that allows you to fix its internal information when you instantiate an instance.

So when we are talking about generating that internal information "randomly", we're normally talking about a convenience method that combines some source of entropy, possibly a data transformer / parser, and the above initializer.

Where should that convenience function live?

If all of the capabilities that you need are provided by your language and its standard library, or the core capabilities that support your domain model anyway, you could make it part of the value object.

But consider this contrived example: that you need not merely a "random" value, but one that requires cryptographically secure randomness that is not available in your standard libraries. Would you really want to introduce a new library dependency in your domain model, or would you prefer to keep such a specialized dependency out of the core of your project?

And if the answer is "we want to keep specialized dependencies out of the core", then the obvious followup question is should the location of the convenience method depend on the details of its internal representation?

My guess is that you should default to an "opt-in" policy for your convenience methods, but that such a policy needs to be balanced against other considerations (like: how much complexity does opt-in add for consumers?)


Design is what we do to get more of what we want than we would get by just doing it. -- Ruth Malan

The trick, in choosing a design, is correctly identifying the "what we want".

VoiceOfUnreason
  • 34,589
  • 2
  • 44
  • 83
1

I don't think the randomness of the value is of any importance when choosing a layer for the generation.

Take your UUID example.

The idea behind GUID/UUID is that you can generate them on the client and not have to worry about collisions. thus making your application more scalable. So generate them in the application layer.

OR

You might be fine putting it in the constructor for your Entity, since its trivial to generate. So your domain layer.

Take your verification code example.

The idea here is that the code provides a proof of verification, So it will have to be generated by the verification system, which is presumably an external "device" or api that you application calls, so the outer layer it is.

Take a true random number you need to be super random.

Maybe you need some special hardware here, so you are making a "device"/outer layer call again

Ewan
  • 83,178
0

This depends on how testable you want these value objects to be. If you need to write deterministic unit tests for these value objects, then generating the random number in the value object prevents you from making any deterministic tests. In this case, a command handler could generate the random number. But then how do you make the command handler testable? Inject a random number generator object to the command handler. And if you can inject a random number generator into a command handler, why not do the same for the value object?

To me, the significant factors are:

  • Random numbers do not lend themselves to writing deterministic tests. Consider defining an interface for an object that provides random numbers so you can mock them for testing purposes.
    • And then inject this random number generator wherever it is convenient for the programmer for testing purposes.
  • If the value object does not need to be tested deterministically, then generate this random number wherever it is most convenient for the programmer.

The fact is you need something random. There is no "random thing" layer in any architecture, because that is a small detail that application architecture isn't concerned with. This could be a consideration at the system or component design level, but here the concern is deterministic testing, not "the right layer for random numbers."

So, first decide where the random number gets generated. This structure can help lead you to the right place to initialize these value objects to begin with. My best advice is to keep it as simple as you can, and think in terms of the business process. Whether this is a concern for the aggregate root or a command handler has more to do with the business requirements than picking an architectural layer.