Commands and Queries¶
Let’s start the tutorial by creating a test to show how to use lato. This test will demonstrate how to
interact with any app built with lato. We’re following a CQRS pattern, where you only need to use app.execute for
the invocation of commands and queries.
Note
CQRS (Command Query Responsibility Segregation) is an architectural pattern that separates the read and write operations of an application. Commands are used to change the state to the application, while queries are used read the state.
With help of pytest, we can create our our first happy path acceptance test:
def test_create_and_complete_todo_scenario(app: Application):
"""This acceptance tests verifies the basic flow of creating and completing a todo"""
app.execute(CreateTodo(todo_id=UUID(int=1), title="Publish the tutorial"))
todos = app.execute(GetAllTodos())
assert len(todos) == 1
app.execute(CompleteTodo(todo_id=UUID(int=1)))
assert len(app.execute(GetSomeTodos(completed=True))) == 1
assert len(app.execute(GetSomeTodos(completed=False))) == 0
Running this code requires the app fixture, and a bunch of commands (CreateTodo, CompleteTodo) and queries (GetAllTodos, GetSomeTodos). Let’s start with
latter ones.
A commands in lato is pydantic data structures with no behavior, which contains all the necessary:
from datetime import datetime
from typing import Optional
from uuid import UUID
from lato import Command
class CreateTodo(Command):
"""This commands represents an intent to create a new todo"""
todo_id: UUID
title: str
description: str = ""
due_at: Optional[datetime] = None
class CompleteTodo(Command):
"""This commands represents an intent to complete an existing todo"""
todo_id: UUID
Take note that the identifier for a new todo, todo_id, is explicitly provided. This simplifies testing
for the app and aligns with the CQRS pattern. By supplying the id upfront, there’s no need to return it,
and we can eliminate the reliance on the database for id generation.
Queries are similar to commands, note that some of the queries does not require any arguments:
from typing import Optional
from uuid import UUID
from lato import Query
class GetAllTodos(Query):
"""A query to get a list of all todos"""
class GetSomeTodos(Query):
"""A query to get specific subset of todos"""
completed: Optional[bool]
class GetTodoDetails(Query):
"""A query to get details of a single todo"""
todo_id: UUID
In the next step we will implement a module responsible for handling the queries and commands.