App Architecture

In this section we’ll look at the different components of our social network app. The goal is to familiarise you enough to feel comfortable extending the code with a new feature in the next section.

There are two main components in the code - the DAML model and the React/TypeScript frontend - with generated TypeScript code to bridge the two. Let’s start by looking at the DAML model, as this sets the core logic of the application.

The DAML Model

Using the Visual Studio Code editor, navigate to the daml subdirectory. There is a single DAML file called User.daml with the model for app users. The core data is at the start of the User contract template.

template User with
    username: Party
    friends: [Party]
  where
    signatory username
    observer friends

There are two important aspects here:

1. The data definition (a schema in database terms), describing the data stored with each user contract. In this case it is an identifier for the user and their current list of friends. Both fields use the built-in Party type which lets us use them in the following clauses.

2. The signatories and observers of the contract. The signatories are the parties whose authorization is required to create or archive instances of the contract template, in this case the user herself. The observers are the parties who are able to view the contract on the ledger. In this case all friends of a user are able to see the user contract.

Let’s say what the signatory and observer clauses mean in our app more concretely. A user Alice can see another user Bob in the network only when Alice is a friend in Bob’s user contract. For this to be true, Bob must have previously added Alice as a friend, as he is the sole signatory on his user contract. If not, Bob will be invisible to Alice.

Here we see two concepts that are central to DAML: authorization and privacy. Authorization is about who can do what, and privacy is about who can see what. In DAML we must answer these questions upfront, as they fundamentally change the design of an application.

The last thing we’ll point out about the DAML model for now is the operation to add friends, called a choice in DAML.

    choice AddFriend: ContractId User with
        friend: Party
      controller username
      do
        assertMsg "You cannot add yourself as a friend" (friend /= username)
        assertMsg "You cannot add a friend twice" (friend `notElem` friends)
        create this with friends = friend :: friends

DAML contracts are immutable (can not be changed in place), so the only way to “update” one is to archive it and create a new instance. That is what the AddFriend choice does: after checking some preconditions, it creates a new user contract with the new friend added to the list. The choice syntax automatically includes the archival of the current instance.

Next we’ll see how our DAML code is reflected and used on the UI side.

TypeScript Code Generation

The user interface for our app is written in TypeScript. TypeScript is a variant of Javascript that provides more support in development through its type system.

In order to build an application on top of DAML, we need a way to refer to our DAML templates and choices in TypeScript. We do this using a DAML to TypeScript code generation tool in the DAML SDK.

To run code generation, we first need to compile the DAML model to an archive format (with a .dar extension). Then the command daml codegen ts takes this file as argument to produce a number of TypeScript files in the specified location.

daml build daml codegen ts .daml/dist/create-daml-app-0.1.0.dar -o daml-ts/src

We now have TypeScript types and companion objects in the daml-ts workspace which we can use from our UI code. We’ll see that next.

The UI

On top of TypeScript, we use the UI framework React. React helps us write modular UI components using a functional style - a component is rerendered whenever one of its inputs changes - with careful use of global state.

The latter is especially interesting as it’s how we handle ledger state in our application. We use a state management feature of React called Hooks. You can see the capabilities of the DAML React hooks in create-daml-app/ui/src/daml-react-hooks/hooks.ts. For example, we can query the ledger for all contracts visible to the logged-in user, create new contracts, and exercise choices.

Let’s see some examples of DAML React hooks.

  const username = useParty();
  const myUserResult = useFetchByKey(User, () => username, [username]);
  const myUser = myUserResult.contract?.payload;
  const allUsers = useQuery(User).contracts;

This is the start of the component which provides data from the current state of the ledger to the main screen of our app. The declarations within MainView all use DAML hooks to get information from the ledger. For instance, allUsers uses a query to get the User contracts on the ledger. However, the query respects the privacy guarantees of a DAML ledger: the contracts returned are only those visible to the currently logged in party. This explains why you cannot see all users in the network on the main screen, only those who have added you as a friend (making you an observer of their User contract).