# Tutorial This tutorial walks through a complete example: creating a data model, setting up a database, and performing operations with CommitDatabase. > **Prerequisites**: This tutorial assumes you have read [DSM](dsm.md) and > understand the basics of DSM syntax and the assemble → parse → introspect > workflow. ## The User/Login Model We'll create a simple model with Users who have Login credentials and Identity information. ### Step 1: Define the Data Model Create a file `model.dsm`: ```dsm namespace Tuto {f529bc42-0618-4f54-a3fb-d55f95c5ad03} { concept User; struct Login { string nickname; string password; }; struct Identity { string firstname; string lastname; }; attachment login; attachment identity; }; ``` ### Step 2: Validate the Model Check the DSM syntax: ```bash python3 tools/dsm_util.py check model.dsm ``` ### Step 3: Create a Database Create a Commit database that embeds the definitions: ```bash python3 tools/dsm_util.py create_commit_database model.dsm model.cdb ``` ### Step 4: Open and Explore Open the database in Python and list the types and attachments it carries: ```{doctest} >>> sorted(str(t) for t in db.definitions().types()) ['Tuto::Account', 'Tuto::Identity', 'Tuto::Login', 'Tuto::Status', 'Tuto::Texture', 'Tuto::Thumbnail', 'Tuto::User'] >>> sorted(str(a).split()[-1] for a in db.definitions().attachments()) ['Tuto::account', 'Tuto::avatar', 'Tuto::identity', 'Tuto::login', 'Tuto::portrait'] ``` ### Step 5: Inject Constants `db.definitions().inject()` makes types accessible as constants in the caller's namespace (already done in this tutorial's setup): ```{doctest} >>> TUTO_A_USER_LOGIN attachment Tuto::login >>> TUTO_S_LOGIN Tuto::Login ``` **Naming convention**: Constants follow the pattern `{NAMESPACE}_{KIND}_{NAME}`: | Kind | Prefix | Example | Description | |-------------|--------|-------------------------|------------------| | Attachment | `_A_` | `TUTO_A_USER_LOGIN` | Attachment type | | Structure | `_S_` | `TUTO_S_LOGIN` | Structure type | | Enumeration | `_E_` | `TUTO_E_STATUS` | Enumeration type | | Concept | `_C_` | `TUTO_C_USER` | Concept type | | Path | `_P_` | `TUTO_P_LOGIN_NICKNAME` | Path to field | ### Step 6: Create a Key and Document Create a new User key. `instance_id()` returns the underlying UUID (randomly generated, so we just check the type here): ```{doctest} >>> key = TUTO_A_USER_LOGIN.create_key() >>> isinstance(key.instance_id(), ValueUUId) True ``` Create a Login document: ```{doctest} >>> login = TUTO_A_USER_LOGIN.create_document() >>> login {nickname='', password=''} >>> login.nickname = "zoop" >>> login.password = "robust" >>> login {nickname='zoop', password='robust'} ``` ### Step 7: Commit to Database Create a mutable state, associate the document with the key, and commit: ```{doctest} >>> mutable_state = CommitMutableState(db.initial_state()) >>> mutable_state.attachment_mutating().set(TUTO_A_USER_LOGIN, key, login) >>> commit_id = db.commit_mutations("First Commit", mutable_state) >>> isinstance(commit_id, ValueCommitId) and len(str(commit_id)) == 40 True ``` ```{important} **The Dual-Layer Contract** CommitDatabase guarantees structural integrity but NOT semantic integrity. When concurrent streams converge, mutations on non-existent documents or unresolved paths are silently ignored. Your application must validate business rules when consuming state. See [The Dual-Layer Contract](commit_contract.md). ``` ### Step 8: Read from Database Read the document back: ```{doctest} >>> state = db.state(commit_id) >>> result = state.attachment_getting().get(TUTO_A_USER_LOGIN, key) >>> result Optional({nickname='zoop', password='robust'}) >>> result.unwrap() {nickname='zoop', password='robust'} ``` ### Step 9: Update a Field Update using a path-based setter. Capture the new commit id returned by `commit_mutations` — the database's mutating APIs don't auto-advance an implicit "current commit" pointer, so chaining commits requires the explicit id: ```{doctest} >>> mutable_state = CommitMutableState(db.state(commit_id)) >>> mutable_state.attachment_mutating().update(TUTO_A_USER_LOGIN, key, TUTO_P_LOGIN_NICKNAME, "zoopy") >>> updated_id = db.commit_mutations("Update Nickname", mutable_state) ``` ### Step 10: View History Read from different commits: ```{doctest} >>> state = db.state(updated_id) >>> state.attachment_getting().get(TUTO_A_USER_LOGIN, key) Optional({nickname='zoopy', password='robust'}) >>> state = db.state(commit_id) >>> state.attachment_getting().get(TUTO_A_USER_LOGIN, key) Optional({nickname='zoop', password='robust'}) >>> state = db.initial_state() >>> state.attachment_getting().get(TUTO_A_USER_LOGIN, key) nil ``` ### Step 11: Inspect Commit Headers ```{doctest} >>> header = db.commit_header(updated_id) >>> header.label() 'Update Nickname' >>> header.parent_commit_id() == commit_id True ``` ## Two Approaches Viper supports two ways to work with your data model: ### Dynamic API (Above) Use Viper's runtime metadata directly: - `DSMBuilder.assemble(path).parse()` → work with types at runtime - Flexible, interpretive, Python-friendly - No code generation needed ### Static API (Generated) Generate infrastructure code from your DSM: ```bash python3 tools/dsm_util.py create_python_package model.dsm ``` This creates a Python package with: - Type-safe classes for concepts and structures - Generated accessors for attachments - IDE autocompletion support ```pycon >>> import model.attachments as ma # Use generated types >>> key = ma.Tuto_UserKey.create() >>> login = ma.Tuto_Login() >>> login.nickname = "user" # Use generated accessors >>> state = CommitMutableState(db.state(db.last_commit_id())) >>> ma.tuto_user_login_set(state.attachment_mutating(), key, login) ``` ```{note} The Static API snippet above requires running Kibo first to generate the `model.attachments` package. It is illustrative — see the [Kibo](../tools/kibo.md) chapter for the full code-generation workflow. ``` Both approaches use the same underlying Viper runtime. ## What's Next - [Database](database.md) - Database and CommitDatabase in detail - [Blobs](blobs.md) - Working with binary data - [Types and Values](types_values.md) - Deep dive into the type system