Command-Query Separation and Identifiers

116 views
Skip to first unread message

Colin Williams

unread,
Apr 18, 2015, 12:56:04 PM4/18/15
to clean-code...@googlegroups.com
I'm hoping people can provide some insight into how to handle an issue I run into frequently with Command-Query Separation. Suppose I have a function that creates a new row in a database, and the return value of the function is an identifier for the new row. While this is technically a violation of this principle, I can't think of a better way to resolve situations where I need to refer to the created row later on. Is there some pattern that will allow me to maintain separation between the comand of adding a row and the query of what row was added?

Colin Williams

unread,
Apr 18, 2015, 1:00:15 PM4/18/15
to clean-code...@googlegroups.com
One thing I've applied successfully is passing a block.  While it doesn't solve every scenario, it helped with my immediate issue.

Mark Badolato

unread,
Apr 18, 2015, 3:03:36 PM4/18/15
to clean-code...@googlegroups.com

On Sat, Apr 18, 2015 at 9:56 AM, Colin Williams <lac...@gmail.com> wrote:
I'm hoping people can provide some insight into how to handle an issue I run into frequently with Command-Query Separation. Suppose I have a function that creates a new row in a database, and the return value of the function is an identifier for the new row

It's a little bit of a different way of thinking as we're so used to using the auto-generated ids from the database. What you probably want to do instead is generate your id (UUID most likely) and pass that into the command along with your other data. You would then persist that id, instead of relying on an auto-generated/sequence id.

mark

Jakob Holderbaum

unread,
Apr 19, 2015, 3:29:40 AM4/19/15
to clean-code...@googlegroups.com
Hi!

I had similar issues on a project buy using string based UUID was not an
option. What we instead did was introducing a concept of a Registrar or
an Identity Management.

One abstract interface that "generates" new IDs for a specific entity.
Like you want to build a new Profile, then ask for an ProfileID and use
this throughout your object generation. After this, is is merely an save
call to your persistence interfaces.

In this system we had a mysql implementation for the Gateway that
persisted entities and their Registrars.

The Registrar would simply create an empty new row and return the ID.
All the storing operations on the Gateway where merely an 'update'.
Meaning that we lost the distinction between creating and updating. This
cleaned up a lot of things.

Overall this was of great success, even for really high load systems.
The doubled call to the persistence layer generates less overhead than I
thought, and besides, most of the times a clean structure and
architecture simply outweighs performance issues.

The only this to consider is to add the abstract concept of transactions
to your system. Every use case call should be encapsulated by an
abstract transaction. This abstraction interface can than be implemented
by your persistence implementations. Especially for mysql was this very
important, otherwise we would end up with empty ID-only rows :)

Cheers
Jakob
--
Jakob Holderbaum, M.Sc.
Embedded Software Engineer

0176 637 297 71
http://jakob.io/
http://jakob.io/mentoring/
h...@jakob.io
@hldrbm

--
Jakob Holderbaum, M.Sc.
Embedded Software Engineer

0176 637 297 71
http://jakob.io/
http://jakob.io/mentoring/
h...@jakob.io
@hldrbm

vivek poddar

unread,
Apr 19, 2015, 8:43:24 AM4/19/15
to clean-code...@googlegroups.com

Hi Jacob,

As always you post is thought provoking which increases the appetite to know more. Hence can you pls provide a little example?

--
The only way to go fast is to go well.
---
You received this message because you are subscribed to the Google Groups "Clean Code Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clean-code-discu...@googlegroups.com.
To post to this group, send email to clean-code...@googlegroups.com.
Visit this group at http://groups.google.com/group/clean-code-discussion.

Caio Fernando Paes de Andrade

unread,
Apr 19, 2015, 11:24:04 AM4/19/15
to clean-code...@googlegroups.com
Jakob,

I've used this approach once too, and I found it to be very interesting.

The database schema is forced to have no rules at all, no NOT NULLs, no foreign key enforcements, which gave me a very high level of freedom in the interaction with the persistence layer.

The only thing I (maybe) did differently was the transaction: my gateway factory took care of that. When main constructed the gateways, it surrounded them with a common transaction object. The usecases knew nothing about the transaction, and the transaction knew nothing about the usecases too.

Unfortunately this is very hard to achieve in cleaning a legacy system, which is my focus right now. :-(

Caio
Message has been deleted

Łukasz Duda

unread,
Apr 19, 2015, 1:05:37 PM4/19/15
to clean-code...@googlegroups.com, mail...@jakob.io
Yeah, but the topic is "Command-Query Separation and Identifiers" and if registrar creates an empty new row and returns the id, isn't it violating the mentioned principle? Does it return new id and change state at the same time? Jakob, could you give an example how do you use such id generator and how do you receive the new id?

I see 4 directions so far:
1. Violate the principle and return modified entity with new id in the save command, which is quite intuitive: User savedUser = gateway.save(user);
2. Use UUID, for example in entity's constructor.
3. Pass the block to receive new id, which I don't recommend, as it is too complicated: gateway.save(user, idReceiver); or maybe gateway.save(user, this);
4. provide getter for last id, introducing temporal coupling, for example: gateway.save(user); gateway.lastId();

The first two are my favorite ones.

Greetings
Łukasz

Caio Fernando Paes de Andrade

unread,
Apr 19, 2015, 1:27:40 PM4/19/15
to clean-code...@googlegroups.com
Sorry for the digression. :-)

I would gladly violate the CQS in this case.

I don't see many problems or consequences. UB and Micah have done the same in the Java Case Study, showing that a compromise is better than blindly following a rule.

After all, CQS is a principle, a guideline, not a silver bullet.

Caio
--

Colin Williams

unread,
Apr 20, 2015, 11:31:30 AM4/20/15
to clean-code...@googlegroups.com, caio...@icloud.com
I appreciate all of the responses. What I'm finding in my current work is that dependence on some identifier is almost like a design smell. Specifically that these identifiers become tendrils that reach into large swaths of code. There are two scenarios I can think of in which you'd want to get back an identifier: when the row will need to be updated again later and when it needs to be referenced in some other part of the database.

If the row needs to be updated again, I feel like these operations should be close to each other. This is the situation I'm currently working with:

id = CreateRow(state="STARTED")
...
UpdateRow(id, state="PROCESSED")

In this case, I found that creating ProcessThing which accepts a block an effective strategy for keeping the identifier isolated.  I'm still violating CQS, but the violation is isolated to a small area rather than distributed across my codebase. More complex scenarios could probably be addressed with something like the Template Method pattern, but at that point it might be worth asking yourself if you really have to modify the same row so many times.

Joining data together, on the other hand, usually looks something like this:

id = CreateFooRow()
CreateBarRow(fooid=id)

In a case like this, it's probably better to describe the relations directly:

bar = Bar()
bar.addFoo(Foo())
Persist(bar) 

In my experience, when I need to violate a guideline, it's far better to restrict the scope of that violation. Also, if later on I gain the flexibility to use UUIDs instead of auto-generated IDs, I only have a few places to change instead of having to hunt all over the codebase.
To unsubscribe from this group and stop receiving emails from it, send an email to clean-code-discussion+unsub...@googlegroups.com.

Jakob Holderbaum

unread,
Apr 21, 2015, 1:04:44 AM4/21/15
to clean-code...@googlegroups.com
Speaking of isolated violations. If you change the mindset just a little
bit here:

id = NextId()

entity = Entity.new(id, "STARTED")
SaveEntity(entity)

// ...

entity.StartProcessing()
SaveEntity(entity)

The lines between creation and updating do blur, and in my opinion this
blurring is something that helps a system to become more descriptive and
less coupled to the persistence strategy.

I mean, there is nothing there in the code that would force you to
create an empty row in a database for the id. It could be a UUID, a
timestamp with nanoseconds, an incrementing counter that is persisted,
or for you first memory based implementation of persistence just a
in-memory counter.

If you think of it like that, the CQRS becomes nearly nonexistent.
NextId reads to me maybe like a query... at least a bit, if you in a
generous mood. :)

I'd also consider whether you actually do need the ID in your system or
your entity. It could be a very local information to your lower
persistence, that is not needed over and over your code base:


entity = Entity.new("Name of it", "STARTED")
SaveEntity(entity)

// ...

entity.StartProcessing()
SaveEntity(entity)


The lookup is internal, the persistence could use different techniques
to map between object and ID. We used unique attribute mappings and also
object identity as criteria, both worked well under certain circumstances.

You can even add a method to retrieve the ID, in case you need it for an
URL or whatnot:

GetIdentifier(entity)


As I said before, this as well could be anything.

Cheers
Jakob
>> Em 19/04/2015, às 14:05, Łukasz Duda <lukasz....@gmail.com <javascript:>>
>> email to clean-code-discu...@googlegroups.com <javascript:>.
>> To post to this group, send email to clean-code...@googlegroups.com
>> <javascript:>.
Reply all
Reply to author
Forward
0 new messages