PythiloLesson 10 of 15 · progress kept in this browser

Commands and queries

A method that changes state and also returns data is hard to reason about: calling it just to look is no longer safe. Keep the two apart. A command changes something and returns nothing. A query returns data and changes nothing, so it can run as often as you like.

class Cart:
    def __init__(self):
        self._items = {}

    def add(self, sku, qty):  # command
        n = self._items.get(sku, 0)
        self._items[sku] = n + qty

    def count(self):  # query
        return sum(self._items.values())

cart = Cart()
cart.add("MUG", 2)
cart.add("PEN", 1)
print(cart.count())

At the application level the same split separates a write side, which runs commands through the domain, from a read side, which answers queries from data shaped for the screen. Commands become small dataclasses that name an intention, each handled in one place.

Your task

Write frozen dataclasses AddTask(task_id, title) and CompleteTask(task_id), and TaskList.execute(command) that applies either one and returns None, raising ValueError for an id that already exists, KeyError for completing an unknown id and TypeError for anything else. Write the queries open_titles(), the titles not yet done in the order they were added, and done_count().