> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ditto.live/llms.txt
> Use this file to discover all available pages before exploring further.

# Migration Guide for Atlas Device Sync

> This guide provides a comprehensive roadmap for migrating from MongoDB Atlas Device Sync to **Ditto**.

## Introduction

By making this transition, you’ll benefit from resilient data synchronization, offline-first capabilities, and efficient data management for a consistent user experience in any environment.

**Why Ditto?**

Ditto offers a decentralized solution for faster, more efficient data synchronization at the edge. Unlike traditional cloud-based systems, Ditto provides an **offline-first architecture** that ensures your app continues to operate smoothly even when internet connectivity is limited or unavailable. We take offline functionality one step further by utilizing **peer-to-peer synchronization** and **mesh networking**. These core capabilities enable your applications to not just operate smoothly in any network environment but sync in real-time without reliance on WiFi, servers, or the cloud.

With just your existing mobile and edge devices, Ditto unlocks,

* **Self-organizing mesh networking** that automatically and securely discovers nearby devices to form wireless networks.
* **Real-time peer-to-peer data sync** within the mesh via Bluetooth Low Energy, Peer-to-Peer Wi-Fi, and Local Area Network, plus opportunistically with the cloud.
* **Offline-first capabilities**, enabling uninterrupted functionality without reliance on network hardware or cloud services.
* **Efficient conflict resolution** optimized to sync only the deltas, ensuring low-bandwidth usage and enabling concurrent edits.
* **Improved latency and performance** by cutting out round-trips to the cloud and enabling direct device-to-device communication.

Please review this guide thoroughly to understand the necessary requirements and changes. By following the outlined steps, your team can successfully migrate to a more robust solution with minimal disruptions.

If you need further assistance at any point, feel free to [reach out to our support team.](https://support.ditto.com)

## Explore our Compatibility Guides

Ditto supports a wide range of platforms, programming languages, and device types, enabling flexible real-time syncing across various environments. Whether you’re developing for iOS, Android, desktop, or integrating devices using different transport methods like Bluetooth or Wi-Fi, Ditto has you covered.

Before migrating, it’s important to review our **Compatibility Guides** to understand all the platforms and languages we support. This will help you understand the options available for your project and ensure your app takes full advantage of Ditto’s peer-to-peer capabilities.

<Card title="Compatibility Guides" icon="ballot-check" href="/sdk/latest/compatibility/compatibility">
  Discover Supported Platforms and Languages
</Card>

## Connecting to MongoDB

Ditto lets your apps sync directly between **clients (Small Peers)** and a  **Ditto Server (**<a href="/cloud/overview" target="_blank">**Cloud Platform**</a>**)** for real-time data sharing, with an offline-first design. This setup reduces the need for constant cloud access and improves performance.

This guide will help you replace **Realm** on the client side to start using Ditto’s peer-to-peer syncing right away. The integration between Ditto Server and MongoDB is enabled by the [MongoDB connector](/cloud/mongodb-connector), which synchronizes data bi-directionally between Ditto and MongoDB.

Unlike other data synchronization systems for MongoDB, Ditto's advanced conflict resolution (based on CRDTs) avoids the need to deploy extra backend services to handle writes and conflicts, just deploy Ditto Server and you have everything you need to integrate with MongoDB.

You can request access to the Connector on the [MongoDB Connector](https://www.ditto.com/platform/mongodb-connector) page.

## Migration Steps

### 1. Get Started: Sign Up and Create Your Ditto App

To get started with Ditto, sign up for an account and create a new project in the Ditto dashboard. You’ll need to grab your API keys to integrate Ditto with your app for real-time syncing.

* **Sign Up for Ditto**: Create an account on [Ditto](https://www.ditto.com)
* **Create an App**: Obtain your app ID and playground token, in the <a href="https://portal.ditto.live" target="_blank">**Ditto portal**</a>**.**

### 2. Remove Realm

Before adding Ditto, we advise you remove MongoDB Realm from your project. This includes uninstalling the Realm SDK and clearing out any Realm-related code and dependencies. Removing Realm at this stage will let you identify all parts of your codebase touching Realm that will need to be migrated to Ditto with the compiler.

<Info>It is also possible to postpone this until later in the process, and effectively run Realm and Ditto in parallel, if desired.</Info>

### 3. Install Ditto

To add Dito to your project, please follow our platform-specific installation guides:

<Card title="SDK Install Guides" icon="cube" href="/sdk/latest/install-guides/install-guides">
  Get started with Ditto
</Card>

These guides will help you install and set up Ditto, ensuring your project is configured correctly for real-time data synchronization.

<Warning>Following the steps in the installation guide is essential to fully utilize Ditto’s capabilities.</Warning>

<Info>By default, Ditto connects and syncs data across nearby devices automatically. If you prefer to sync only between your device and the cloud, you can disable local sync by turning off LAN, peer-to-peer Wi-Fi, and Bluetooth transports. To learn more see: [`Managing the Mesh > Customizing Transports`](/sdk/latest/sync/customizing-transport-configurations)</Info>

### 4. Update Your Data Models

With Realm your models are tightly coupled to Realm’s classes. This is not required with Ditto.

Make sure to review your data types, relationships, and primary keys, and update them where necessary to work seamlessly with Ditto’s peer-to-peer sync architecture.

Before (Realm model):

<CodeGroup>
  ```swift Swift theme={null}
  final class Todo: Object, ObjectKeyIdentifiable {
      @Persisted(primaryKey: true) var _id: ObjectId
      @Persisted var title = ""
      @Persisted var completed = false
  }
  ```

  ```kotlin Kotlin theme={null}
  class Todo() : RealmObject {
      @PrimaryKey
      var _id: ObjectId = ObjectId()
      var title: String = ""
      var completed: Boolean = false
  }
  ```

  ```java Java theme={null}
  public class Todo extends RealmObject {
      @PrimaryKey
      private ObjectId _id = new ObjectId();
      private String title = "";
      private boolean completed = false;

      // getters and setters
  }
  ```
</CodeGroup>

After (Ditto model):

<CodeGroup>
  ```swift Swift theme={null}
  final class Todo: Codable {
      var _id = UUID()
      var title = ""
      var isCompleted = false
      var isDeleted = false
  }
  ```

  ```kotlin Kotlin theme={null}
  data class Todo(
      val _id: String,
      val title: String,
      val isCompleted: Boolean = false,
      val isDeleted: Boolean = false
  )
  ```

  ```java Java theme={null}
  public class Todo {
      private String _id;
      private String title;
      private boolean isCompleted;
      private boolean isDeleted;

      public Todo(String _id, String title, boolean isCompleted, boolean isDeleted) {
          this._id = _id;
          this.title = title;
          this.isCompleted = isCompleted;
          this.isDeleted = isDeleted;
      }

      // getters and setters
  }
  ```
</CodeGroup>

There are a few conventions with Ditto data models worth considering:

* Document ID keys are called `_id` and are usually UUIDs.
* Timestamps are strings in ISO 8601 format.
* Ditto will either return items as `Map<String, Any>` or as a JSON string, which you may consider deserializing as native data types.

We also recommend you make yourself familiar with [REGISTER](/key-concepts/document-model#registers), [MAP](/key-concepts/document-model#maps), and [ATTACHMENT](/key-concepts/document-model#attachments) data types. Read more about best practices for data modelling with Ditto in [this guide](/best-practices/data-modeling).

### 5. Set Up Subscriptions

You can replace Realm's sync logic with Ditto subscriptions to keep data in sync between your devices in real-time.

<CodeGroup>
  ```swift Swift theme={null}
  let query = "SELECT * FROM todos"
  var subscription = try ditto.sync.registerSubscription(query: query)
  ```

  ```kotlin Kotlin theme={null}
  val todosSubscription = ditto.sync.registerSubscription(
      query = "SELECT * FROM COLLECTION todos"
  )
  ```

  ```java Java theme={null}
  String query = "SELECT * FROM todos";
  DittoSyncSubscription subscription = ditto.getSync().registerSubscription(query);
  ```
</CodeGroup>

* In order for Ditto to sync data between client devices and Ditto Server you will need to register a subscription for each collection you would like to sync.
* Start your subscription as early in the application lifecycle as possible.
* Keep a reference to the created subscription, so you can manage it and it doesn’t get garbage collected.

### 6. Migrate Your Data Operations

Now, update the create, read, update, and delete (CRUD) operations in your app. Replace Realm’s methods with Ditto’s, ensuring all your data interactions are using Ditto’s sync logic.

#### **Create**

When adding new items, use the execute method on the `ditto.store` object to insert your data. This ensures new records are synced efficiently across peers in real-time.

<CodeGroup>
  ```swift Swift theme={null}
  func addTodo(title: String) {
      let newTodo = Todo(title: title)
      ditto.store.execute(query: "INSERT INTO todos VALUES (:todo)",
  		    			arguments: ["todo": newTodo])
  }
  ```

  ```kotlin Kotlin theme={null}
  suspend fun addTodo(title: String) {
      val newTodo = Todo(
          _id = UUID.randomUUID().toString(),
          title = title
      )
      ditto.store.execute(
              query = "INSERT INTO todos VALUES (:new)",
              arguments = mapOf(
  			    "new" to newTodo.asMap() // here you would have some code to serialize your class as a Map<String, Any>
              )
          )
  }
  ```

  ```java Java theme={null}
  public void addTodo(String title) {
      Todo newTodo = new Todo(
          UUID.randomUUID().toString(),
          title,
          false,
          false
      );

      Map<String, Object> args = new HashMap<>();
      args.put("new", newTodo.asMap()); // serialize your class as a Map<String, Object>

      ditto.getStore().execute("INSERT INTO todos VALUES (:new)", args);
  }
  ```
</CodeGroup>

#### **Read**

Refactor how you retrieve data by leveraging Ditto’s powerful observation capabilities.

When reading data in Ditto, the primary method is to set up an <a href="/sdk/latest/crud/observing-data-changes" target="_blank">**observer**</a> that listens for changes and updates in real-time. This ensures your app automatically reflects the latest data from other peers.

**Real-Time Observing**

This allows your app to respond to data changes in real-time by syncing updates across all connected devices.

<CodeGroup>
  ```swift Swift theme={null}
  private func observeTodos() {
      return ditto.store.registerObserver(
          query: "SELECT * FROM todos WHERE isDeleted = :isDeleted",
  		arguments: ["isDeleted": false]) { results in
  	// Decode the results
  	}
  }
  ```

  ```kotlin Kotlin theme={null}
  suspend fun observeTodos(): Flow<List<Todo>> {
  	return ditto.store.registerObserverAsFlow(
  			query = "SELECT * FROM todos WHERE isDeleted = :isDeleted",
  			arguments = mapOf(
  				 "isDeleted" to false
  			 )
  		  )
  }
  ```

  ```java Java theme={null}
  private void observeTodos() {
      Map<String, Object> args = new HashMap<>();
      args.put("isDeleted", false);

      ditto.getStore().registerObserver(
          "SELECT * FROM todos WHERE isDeleted = :isDeleted",
          args,
          (queryResult, event) -> {
              // Decode the results
          }
      );
  }
  ```
</CodeGroup>

**One-Off Reads**

In addition to real-time observations, Ditto allows for **one-time reads**. These are useful when you need to fetch data at a specific point in time without continuously listening for updates. You can do this by using the `execute` method on the `ditto.store` using the same query (for example: `“SELECT * FROM todos WHERE isDeleted = false”`)

#### **Update**

When updating data, use UPDATE on the `ditto.store` object. Ditto lets you sync the minimum data needed, ensuring that all peers converge on the same version of the data across the network.

<CodeGroup>
  ```swift Swift theme={null}
  func update(_ todo: Todo) {
      ditto.store.execute(
          query: "UPDATE todos SET isCompleted = :isCompleted WHERE _id = :_id",
          arguments: ["_id": todo._id, "isCompleted": !todo.isCompleted]
      )
  }
  ```

  ```kotlin Kotlin theme={null}
  suspend fun toggleIsComplete(todo: Todo) {
      dittoStoreManager.executeQuery(
          dittoQuery = "UPDATE todos SET completed = :isCompleted WHERE _id = :_id",
          arguments = mapOf(
              "isCompleted" to !todo.completed,
              "_id" to todo._id
          )
      )
  }
  ```

  ```java Java theme={null}
  public void update(Todo todo) {
      Map<String, Object> args = new HashMap<>();
      args.put("_id", todo.get_id());
      args.put("isCompleted", !todo.isCompleted());

      ditto.getStore().execute(
          "UPDATE todos SET isCompleted = :isCompleted WHERE _id = :_id",
          args
      );
  }
  ```
</CodeGroup>

#### **Delete**

In a distributed data system like Ditto, deletion follows a “<a href="/sdk/latest/crud/delete#soft-delete-pattern" target="_blank">soft
delete</a>” pattern. This means that when you mark an item as deleted, it’s not
immediately removed from the collection or local storage. Instead, the item is
flagged as deleted to ensure that other devices in the network remain in sync.
To fully remove the item and free up space, you’ll need to evict these “soft
deleted” items. For more details, see the section on <a href="/best-practices/device-storage-management" target="_blank">Evictions</a>.

<CodeGroup>
  ```swift Swift theme={null}
  func delete(_ todo: Todo) {
      ditto.store.execute(
          query: "UPDATE todos SET isDeleted = :isDeleted WHERE _id = :_id",
          arguments: [String: Any] = ["_id": todo.id, "isDeleted": true]
      )
  }
  ```

  ```kotlin Kotlin theme={null}
  suspend fun deleteTodo(todo: Todo) {
        ditto.store.execute(
            dittoQuery = """UPDATE todos
  						  SET isDeleted = :isDeleted
  						  WHERE _id = :_id"""
            arguments = mapOf(
                "isDeleted" to true,
                "_id" to todo.id
            )
        )
    }
  ```

  ```java Java theme={null}
  public void delete(Todo todo) {
      Map<String, Object> args = new HashMap<>();
      args.put("_id", todo.get_id());
      args.put("isDeleted", true);

      ditto.getStore().execute(
          "UPDATE todos SET isDeleted = :isDeleted WHERE _id = :_id",
          args
      );
  }
  ```
</CodeGroup>

### 7. Ensure your Views Respond to Updates

Your app needs to react to changes in real-time data. Set up <a href="/sdk/latest/crud/observing-data-changes" target="_blank">observers</a> to make sure your UI updates when the data synced through Ditto changes.

### 8. Evictions

In distributed systems, it’s important to manage storage efficiently, especially when dealing with large datasets or limited device space. **Evictions** allow you to remove data that is no longer needed, freeing up space in a controlled way while maintaining the integrity of your app’s data.

To manage storage, Ditto allows you to configure **eviction policies**. This lets you free up space by removing data that is no longer needed or marked as deleted (as described in the **soft delete** section).

For more on evictions and deletion, refer to Ditto’s documentation: [Evictions and Delete](/sdk/latest/crud/delete).

### 9. Authentication and Authorization

A production application must have robust auth system in place. Ditto lets you rebuild your Atlas Device Sync authentication and authorization flow, as explained below.

## Ditto Auth vs Device Sync Auth

### Components

* **Ditto**: The [Ditto SDKs](/sdk/latest/install-guides/install-guides) and [Ditto Cloud](/cloud/overview) services (backend). Developers interact with Ditto via the SDK on the device and via the [Ditto Portal](https://portal.ditto.live/) in the cloud.
* **Customer Components**: The application (app), the authorization endpoint (a backend service), and the Identity Provider (IdP).
* **Identity Provider (IdP)**: This is the service that manages user identities. User logs in, and the IdP issues them a token, that is used elsewhere in the system to represent that user. IdP can be either a fully managed third-party service (for example Auth0), or a service maintained by the customer. The login method depends on user’s preferences and the IdP's features and requirements—from straightforward username/password to social login with Google or Facebook, magic email links, multi-factor authentication, and more.
* **Authorization endpoint** is a service deployed and accessible from the web; its URL is stored in the Ditto portal. It’s responsible for assigning user access permissions to documents and collections in Ditto, and validating user tokens.

### Auth Flow

```mermaid theme={null}
sequenceDiagram
    actor User
    participant App as Mobile App
    participant IdP as Identity Provider
    participant Ditto as Ditto
    participant AuthEndpoint as Authorization Endpoint

    %% Authenticated Session %%
    note over App,Ditto: **Authenticated Flow**

    User ->> App: Chooses to Log In

    App ->> IdP: Redirect to Authenticate
    User ->> IdP: Enters Credentials
    IdP -->> App: Return Identity Token


    App ->> Ditto: Login to Ditto with identity token
    Ditto ->> AuthEndpoint: Forward Identity Token
    AuthEndpoint ->> IdP: Validate Identity Token
    AuthEndpoint ->> AuthEndpoint: Assign Authenticated Permissions
    AuthEndpoint -->> Ditto: Return JWT with Permissions
    Ditto ->> Ditto: Sign JWT
    Ditto -->> App: Establish authenticated session

    User ->> App: Continues Interaction
    App ->> Ditto: Sync Data (Authenticated Permissions)
    Ditto ->> App: 

```

* To establish identity in the app, the user performs a login action with their identity provider.
* The Ditto SDK requires the user token to call the `ditto.login` method.
* When `ditto.login` is called, Ditto forwards the user token to the customer's authorization endpoint.
* Commonly, after login, the IdP generates an identity token containing the user ID and signs it with the IdP's private key. The [JSON Web Token (JWT)](https://jwt.io/) format is commonly used.
* The customer's authorization endpoint needs to accept HTTP requests from Ditto with the JWT as the payload, and:
  * **Verify the JWT's authenticity**: This is typically done by verifying the JWT's signature using the IdP's public key, either via an SDK method or an API call to the IdP's token validation endpoint.
  * **Respond to Ditto with a new JWT containing permissions**: This JWT contains the read and write permissions for Ditto collections specific to that user. This often involves a database lookup for the user's role and set of permissions, and transforming them into Ditto's expected format.
* Ditto backend will sign the received JWT with its own certificate and pass it back to the customer application. This completes the login flow and allows the logged-in user to interact with Ditto.

### Differences and Similarities Between MongoDB Atlas App Services and Ditto Implementations

#### Authentication

**Device Sync**

The now-deprecated MongoDB Atlas App Services bundled a way to access some identity providers and a permissions layer. Bundled IdPs included common providers like email and OpenID (such as Google).

**Ditto**

Ditto doesn't bundle an identity provider; the customer always brings their own.

**Both**

Many customers who operate their own custom IdP would need to integrate it in both Atlas and Ditto.

#### Authorization

**Device Sync**

Permissions and roles were defined and stored directly in Atlas, in MongoDB's JSON-like format.

**Ditto**

Permissions are assigned via a webhook invocation from Ditto to the customer's authorization service that returns a JWT with permissions to Ditto.

User roles and permissions can be stored anywhere—for example, in the customer's own custom IdP solution.

## Additional Resources

* Need assistance? <a href="https://support.ditto.com" target="_blank">Reach out to us</a>!
* Check out our <a href="/best-practices" target="_blank">Best Practices</a>
* Learn more about our <a href="https://www.ditto.com/platform/mongodb-connector" target="_blank">MongoDB Connector</a> coming soon.
