57

My take on CQRS is when followed strictly your commands don't return anything (return type void), so my example is really straight forward: How do you retrieve an ID when creating something?

For example, when creating a credit card transaction it seems rather important to return a transaction ID, or when creating a customer it would be much easier if you got the customer you created or the customer ID back so a browser could navigate automatically to that customer page for example.

One solution could be to first ask for an ID and then create the customer or transaction with that ID, but it seems pretty weird.

Does anyone have any experience with this or know how it should be done in the most effective way? Maybe I have misunderstood something?

Rob Johnston
  • 859
  • 8
  • 21
Tomas Jansson
  • 22,767
  • 13
  • 83
  • 137

2 Answers2

35

CQRS is all about fire-and-forget, and since GUIDs are very reliable (low risk of collision) there is no problem sending in a GUID that you generate your self.

The steps would basically be:

  1. Create your command
  2. Generate and assign your identity (GUID) to it
  3. Fire the command
  4. Return the identity earlier generated

Read more about GUIDs on Wikipedia

Robin Orheden
  • 2,714
  • 23
  • 24
  • 2
    Cant see a reason why it would. – Robin Orheden Dec 06 '10 at 03:06
  • Well, you are modifying state *and* returning a value in the same call: that's the definition of a CQRS violation. – RBarryYoung Dec 06 '10 at 03:10
  • 1
    I would not say that you're modifying state. And returning a value? That was merely an expression. The command will never return anything. As long as the GUID is passed into the command I do not see a violation. – Robin Orheden Dec 06 '10 at 03:15
  • No, no, no. When you **generate** the GUID, you *are* modifying the state of the systems GUID generator, *and* returning a value (the new GUID) at the same time. – RBarryYoung Dec 06 '10 at 03:24
  • 2
    The system GUID generator does not have state. Creating a GUID is a straight forward readonly computation. But I might be wrong? Would be happy to see your entry here next to mine. – Robin Orheden Dec 06 '10 at 06:15
  • I think generating a GUID in the client seems wrong, even though the likelihood of collision is very small. Let say you have a public service, identification belongs in the business logic I think. Also, why do you need step 4 in your description? Wasn't it all about fire-and-forget? So why are you returning if you already have generated the GUID. – Tomas Jansson Dec 06 '10 at 06:41
  • 1
    Thinking that it's wrong - and being wrong are two separate things. There are many people who use GUIDs this way in their CQRS implementations today. Are they wrong? And since there can be about 2^128 unique keys, the risk of a collision I would say would is _extremely_ small. I don't see the issue there. The idea is to generate your GUID when firing your command - outside your command - where do you do that? Your business. – Robin Orheden Dec 06 '10 at 10:27
  • Ok, let us say you have a credit card transaction system that you would like to expose through some kind of service interface. Should the clients calling that interface be responsible for generating the GUIDs? Of course there should be some security checks that the GUID is unique, but if you have a service that you're exposing you can't be sure that the callee is really generating true GUIDs. But maybe CQRS is not suitable for this situation? – Tomas Jansson Dec 06 '10 at 11:52
  • 1
    It feels like you're approaching it from the wrong point of view. The clients would never interact directly with the commands. That would be dangerous. There would be a layer which did that. This layer would also generate the GUID and return it back to the client. – Robin Orheden Dec 06 '10 at 16:34
  • 1
    Still, I think it is weird for the "client" to generate the ID. What if you don't want to have GUIDs as IDs? GUIDs doesn't look that good in a url, and nice looking urls are important too in many scenarios. – Tomas Jansson Dec 07 '10 at 06:52
  • 2
    I would say that "nice looking URLs" is the worst argument I've heard in a long time. There is a database pattern (cant remember it's name) but it's one where you create a map between GUIDs(or an identity of some sort) and integer primary keys. This kind of pattern would probably suit for that kind of situation. – Robin Orheden Dec 08 '10 at 23:07
  • 11
    People argueing GUID generation at the "client" is "dangerous" or "weird" have bigger problems, not done any real world CQRS implementation, and are focusing on a problem that's not even there. – Yves Reynhout Dec 10 '10 at 20:25
  • And FYI, GUID generation is **not** normally stateless in any implemented architecture that I know of. – RBarryYoung Dec 11 '10 at 13:46
  • 4
    @RBarryY - I think you're mixing up the architectural CQRS pattern with CQS. Anyway, it's perfectly normal to use GUIDs for this sort of thing. If you need a 'friendly' integer ID then just generate it and treat it as an attribute of your entity and use that in the URLs instead of the 'actual' GUID ID which the application uses internally. – FinnNk Dec 16 '10 at 21:30
  • An alternative approach is to let the client try to create/choose a readable id. If that id is available it will show later when the view that keeps a map from readable id -> internal id. – Tomas Jansson Feb 10 '14 at 09:16
  • What about passing a client generated 'transaction id' of some sort to the command when creating the entry and have a query for getting the generated entry id based on the transaction id. This way, generation of the entry id is encapsulated into the business logic. The issues of storing the relation between transaction id and entry id or transaction id collision can be easily handled. – Cristian Toma Feb 11 '15 at 21:58
  • How about GUIDs as primary keys (vs int32/int64) lead to up to 80 slower DB performance? – Dima Jun 23 '17 at 16:51
0

Integer id's / GUIDs / byte arrays of any size can be reliable enough at practice, but they all do not correspond to the theoretical requirement (collisions happens), while valid theoretical solution exists and can be applied most of the time.

I'd formulate the solution as: in the equal-level system co-operation one's identity should be guaranteed by the system of a higher level. Higher-level system is the one which manages the lifetime of co-operating systems.

Example:

class John
{
    private readonly int id;

    public John(int id)
    {
        this.id = id;
    }

    public void UseSite(Site site)
    {
        site.CreateAccount(id, "john");

        site.SetPassword(id, "john", "123");

        /* ... */
    }
}

class Site
{
    public void CreateAccount(int humanId, string accName) { /* ... */ }

    public void SetPassword(int humanId, string accName, string pwd) { /* ... */ }

    /* ... */
}

class Program
{
    static void Main(string[] args)
    {
        Site s = new Site();

        // It's easy to guarantee the identity while there's only one object 
        John j = new John(4);

        Console.ReadLine();
    }
}

Program is the higher-level module. It is responsible to use John and Site correctly. Providing John with an unique identifier is a part of this responsibility.

You will find that it is impossible or very hard to deal with the identity of some real-life systems, like a human. It happens when these systems are on the same level as your system. Typical example is a human and a web-site. Your site will never have a guarantee that the right human requesting the page. In this case you should use the probability-based approach with a reliable hash.

astef
  • 8,575
  • 4
  • 56
  • 95
  • Thanks for answering this long time after. I have however come to realize that it is not a problem to even let the client generate the ids as long as there is a server side check. I actually write about it in this post: http://blog.tomasjansson.com/cqrs-the-simple-way-with-eventstore-and-elasticsearch-build-the-api-with-simple-web/ which is part of a larger blog serie. – Tomas Jansson Jul 15 '14 at 14:02