Design apps

As you come up with ideas for applications, you are going to make many design decisions about how to structure and organize your project. Most of these design decisions are independent of whether you deploy your app on a traditional cloud service or on the Internet Computer. However, if you are going to deploy on the Internet Computer, there are a few design decisions that you should pay particular attention to as you plan the implementation for your app.

This section is work-in-progress and incomplete. As best practices and design patterns evolve for building applications that run on the Internet Computer, the information included here will also evolve and change accordingly.

Single or multiple canister architecture

One of the first decisions you might want to consider when designing your application is whether it should be encapsulated in a single canister or consist of multiple canisters.

For example, if you are writing a simple service with no front-end, you might want to use a single canister to simplify project management and maintenance and focus on adding features. If your application has both front-end assets and back-end business logic, your project is likely to consist of at least two canisters, with one canister for managing user interface components and another canister for the backend services the application provides.

In planning, you might also consider placing some common reusable services in their own canister so that they can be imported and called from other more-specialized canisters or made available for other developers to use. The LinkedUp sample application illustrates this approach by splitting the professional service application into two canisters. In the LinkedUp example, the functions that establish social connections are defined in the connectd canister and separate from the functions used to set up professional profiles that are defined in the linkedup canister. It is easy to imagine extending the application with a third canister, for example to schedule events based on profile attributes or shared connections.

Segregating actors from types and utilities

In planning the architecture for your project, one common practice is to place the code for the main actor in one file with separate additional files for defining the types you program uses and utility functions that don’t require an actor.

For example, you might set up the back-end logic for your application to consist of the following files:

  • Main.mo or main.rs with the functions that require an actor to send query and update calls.

  • Util.mo or util.rs with helper functions that can be imported for the actor to use.

  • Types.mo or types.rs with all of the data type definitions for your application.

Using query calls

As discussed in Query and update methods, queries return results faster than update calls. Therefore,explicitly marking a function as a query is an effective strategy for improving application performance. In the planning and design phase, you should consider how best to use query calls instead of functions that can perform queries or updates.

That is a good general rule to follow and can be applied broadly to most categories of applications. However, you should also consider the security and performance trade-off that queries don’t go through consensus. For some applications, that trade-off might be appropriate. For example, if you are developing a blogging platform, queries that retrieve articles matching a tag probably don’t warrant going through consensus to ensure that a majority of nodes agree on the results. However, if your application is retrieving sensitive information—like financial data—you might want more assurance about your results than a basic query provides.

As an alternative to basic queries, the Internet Computer also supports certified queries. Certified queries enable you to receive authenticated responses that you can trust. Using certified queries is an advanced technique that is not covered in the tutorials or other developer-focused documentation, but you can learn about how the authentication works and what you need to do to configure your program to return certified data in response to queries in the Interface specification.

Data storage and retrieval

For many applications, databases are primarily used to provide long term storage of records that can be retrieved efficiently using one or more key fields. On the Internet Computer, this use case is usually addressed through data structures like hash tables. Instead of a traditional database, the Internet Computer enables you to use stable memory to handle long-term data storage—often referred to as orthogonal persistance—and to use query calls to retrieve your data. Although this approach works for most applications and simple key value database requests it doesn’t fully emulate a relational database.