How to handle unique constraint in Entity (Aggregate Root)?

In our case we are creating a Ticket entity and in that Email Id has to be unique, how to achieve this using the Lagom Framework?

1 Like

Hope this helps:

We use a read side check with a risk that two users could use the same email while read side is not consistent.
For now this never heppend in our case

Another solution I can think of is making use the registry of PEs Lagom maintains.

Let me explain!

Service implementations get reference to Lagom PE by supplying a unique entity id to the PE registry.

  private def entityRef(id: Long) = persistentEntityRegistry.refFor[TicketPersistenceEntity](id.toString)

The way this API seems to work is that when you invoke entityRef with a unique id ticket123 for the first time, it internally creates a map like so, (ticket123 -> persistentEntityRef). For every subsequent time this API is invoke with the same id, this map is refered to return the persistentEntityRef already created.

This gives us an opportunity to fiddle with this map by supplying ids of our choice. What if we choose to provide an id of ticket123_helloworld@gmail.com? This exactly is the solution I propose.

When you try to refer any ticket just provide the associated mail id too. For example, if you wish to delete this ticket via a rest api call, just supply the mail id as part of body or the path param itself.

DELETE /api/ticket/ticket123_helloworld@gmail.com

Let me also explain the issues with this approach:

  1. If you decide to use the path param approach:
  • If there are two or more such attributes on which uniqueness constraint needs to be implemented, the string could get fairly large making it difficult to test.
  • This approach can only be used if the APIs are internal and are not public. Because clearly, it is only the registry that we are fiddling with and not the rest of the models such as Ticket, KafkaEvents, PE Command/Event/State and Readside models i.e., Ticket still is identified uniquely only with id on read side while write side is being uniquely identified with id_email. Thus, making the APIs inconsistent & unintuitive.
  1. If you decide to use the query param approach: It may not be feasible to use this approach when working with APIs that involve non ticket entities which belong to ticket. For example, ticket may be channelised through different departments and providing email id may not make sense when updating department details.
   restCall(Method.PUT, "/api/tickets/:id/department", updateDepartment _)
   
   def updateDepartment(ticketId: Long): ServiceCall[Department, Done]

Cleary this solution doesn’t seem to scale and is more of a hack but definitely gets the job done fairly reasonably in small cases where SAGA pattern could be an overkill.

Comments and suggestions are welcome.