Synchronization Engine & Domain

HTTP replication (Pull & Push), workspace connections, and the time-entry lifecycle.

Synchronization Engine & Domain

Mr. Tick uses a provider-agnostic synchronization engine based on HTTP Replication (Pull & Push of Deltas). Rather than opening persistent connections such as WebSockets or aggressively polling third-party APIs, it synchronizes data in a structured, efficient way that tolerates network failures.


πŸ”„ How HTTP Replication Works

Synchronization between Mr. Tick and a server or management tool (Redmine, Jira, YouTrack, etc.) has two distinct cycles: Pull and Push.

       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
       β”‚     Local DB (Mr-tick)        β”‚
       β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–²β”€β”€β”€β”€β”€β”€β”˜
              β”‚                β”‚
       [1] Pull (Download)     β”‚ [2] Push (Upload)
       Tasks, Categories,      β”‚ New Time Entries,
       Metadata & Checkpoints  β”‚ Updates and Deletes
              β”‚                β”‚
              β–Ό                β”‚
       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”
       β”‚   Remote Server (API)        β”‚
       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

1. Pull Phase (Downloading Data & Deltas)

Pull retrieves only remote data created or updated since the last synchronization.

Executor Services:

  • TaskPullService: Downloads tasks/issues assigned to the authenticated member.
  • TimeEntriesPullService: Downloads time entries already stored on the server.
  • MetadataPullService: Downloads essential metadata such as allowed activities, categories, and projects.

checkpoint and batch:

To avoid overloading external APIs or repeatedly downloading the entire history:

  • Mr. Tick sends a checkpoint, which can be an ISO 8601 timestamp or cursor.
  • The DataSource API returns changes after that checkpoint, limited by batch size (for example, 50 items per page).
  • Once a batch is processed successfully, Mr. Tick stores the new checkpoint locally.
// Exemplo conceitual do mΓ©todo pull na interface de consulta
public async pull(memberId: string, checkpoint?: string, batch: number = 50): Promise<TaskDTO[]> {
  const url = checkpoint
    ? `/api/tasks?updated_after=${checkpoint}&limit=${batch}`
    : `/api/tasks?limit=${batch}`

  const response = await httpClient.get(url)
  return response.data.map(mapToTaskDTO)
}

2. Push Phase (Uploading Time Entries & Detecting Conflicts)

Push sends local time entries that were created, edited, or deleted to the remote provider through TimeEntriesPushService.

Batch Processing Flow:

Every batch item (SyncTimeEntryDTO) passes through a strict pipeline:

  1. Document Validation:
    • A document must have valid id and updatedAt values.
  2. Deletion Handling (_deleted: true):
    • When an entry is deleted locally, Mr. Tick calls timeEntryRepository.delete(id) on the remote server and marks it as synchronized (syncedAt: new Date()).
  3. Conflict Detection (Optimistic Concurrency Control):
    • When a record already exists remotely, Mr. Tick compares the server's updatedAt with the client's assumedMasterState.updatedAt.
    • No Conflict: Compatible records have their times and comments updated remotely and confirmed with syncedAt.
    • Conflict: If someone modified the remote record after the last local read, the item is marked conflicted: true with { server: existing, local: entry } for safe resolution without data loss.
  4. Creating New Time Entries:
    • When a record does not exist remotely, the TimeEntry entity is validated and persisted through timeEntryRepository.create().

🏒 Workspaces & DataSource Connections

In Mr. Tick, all data context belongs to a Workspace.

Workspace Responsibilities:

  • A workspace provides tenant isolation (for example, "Company A", "Company B", "Freelance Consulting").
  • Each workspace has its own tasks, time-entry history, and preference settings.

Connection Structure (DataSourceConnection):

A workspace can have multiple connections to external tools:

type DataSourceConnection = {
  id: string // Identificador da conexΓ£o (ex: "conn-jira-prod")
  dataSourceId: string // ID do plugin (ex: "jira-datasource")
  status: 'connected' | 'disabled' | 'disconnected'
  member?: {
    // Dados do usuΓ‘rio autenticado nessa ferramenta
    id: string
    name: string
    login: string
    avatarUrl?: string
  }
  config?: Record<string, unknown> // ConfiguraΓ§Γ΅es especΓ­ficas (ex: baseUrl, tenant)
}

IDataSourceResolver receives the workspaceId and connectionInstanceId, instantiates the correct adapter at runtime, and injects secure credentials.


⏱️ Domain Entities: Time Entries and Tasks

The @mr-tick/domain layer ensures consistency of time data:

TimeEntry Entity

Required validation rules:

  • Time Calculation (timeSpent): When startDate and endDate are provided, duration in seconds is validated against the actual date interval. End dates cannot precede start dates.
  • Comments: An optional field limited to 255 characters, with whitespace sanitization.
  • Activity & Task: Every time entry must reference a task and an activity (a work category such as "Development", "Meeting", or "Code Review").

Task Entity

  • Contains a title (at least 3 characters), description, workspaceId, and optional external-ID references (externalId, externalType).
  • Fallback Task (createFallback): When a user starts a timer without selecting a task, Mr. Tick creates a fallback General Task so worked time is not lost.