Documentation Index
Fetch the complete documentation index at: https://mintlify.com/marcosfabricio3/simple-manager-mobile/llms.txt
Use this file to discover all available pages before exploring further.
Data Flow Overview
Data flows through the application in a unidirectional pattern, ensuring predictable behavior and making the system easier to reason about.
Flow Diagram
Data Flow Patterns
Create Operation
User Input
User fills a form and submits in the UI component
Hook Call
UI component calls the create() method from useRecords hook
Service Processing
Service generates UUID, timestamps, and creates Record entity
Repository Persistence
Repository executes SQL INSERT statement
Database Storage
SQLite stores the record
State Refresh
Hook reloads data and updates component state
UI Update
Component re-renders with updated data
Read Operation
Component Mount
Component mounts and hook’s useEffect runs
Load Data
Hook calls load() which invokes service.list()
Repository Query
Repository executes SELECT * FROM records WHERE isDeleted = 0
Data Mapping
Repository maps database rows to Record entities
State Update
Hook updates local state with records
Render
Component renders the list of records
Update Operation
Edit Action
User modifies a record and saves changes
Hook Update
UI calls update(record) from hook
Timestamp Update
Service updates the updatedAt timestamp
Repository Update
Repository executes SQL UPDATE statement
Refresh
Hook reloads all records to reflect changes
Re-render
UI updates with modified data
Delete Operation (Soft Delete)
Delete Action
User confirms deletion
Hook Call
UI calls remove(id) from hook
Service Delete
Service calls repository’s softDelete(id)
Flag Update
Repository sets isDeleted = 1 via UPDATE query
Refresh
Hook reloads records (deleted ones are filtered out)
UI Update
Component re-renders without the deleted record
Complete Example: Creating a Record
Let’s trace a complete data flow for creating a new record:
1. UI Component (Presentation Layer)
import { useRecords } from '@/src/presentation/hooks/useRecords';
export function RecordsScreen() {
const { records, create } = useRecords();
const [title, setTitle] = useState('');
const handleCreate = async () => {
try {
// Step 1: Call hook's create method
await create(title, 'client');
setTitle(''); // Clear input
} catch (error) {
alert(error.message);
}
};
return (
<View>
<TextInput value={title} onChangeText={setTitle} />
<Button title="Create" onPress={handleCreate} />
{records.map(record => <RecordCard key={record.id} record={record} />)}
</View>
);
}
2. Custom Hook (Presentation Layer)
import { RecordService } from '@/src/application/services/RecordService';
export function useRecords() {
const service = useMemo(() => new RecordService(), []);
const [records, setRecords] = useState<Record[]>([]);
const load = async () => {
// Step 6: Reload data after creation
const data = await service.list();
setRecords(data); // Step 7: Update state
};
const create = async (title: string, type: string) => {
// Step 2: Forward to service
await service.create(title, type);
await load(); // Step 5: Refresh after creation
};
return { records, create, load };
}
3. Service (Application Layer)
import { RecordRepository } from '@/src/infraestructure/repositories/RecordRepository';
import * as Crypto from 'expo-crypto';
export class RecordService {
private repository = new RecordRepository();
async create(title: string, type: string) {
// Step 3: Business logic - generate ID and timestamps
const now = new Date().toISOString();
const record: Record = {
id: Crypto.randomUUID(),
title,
type,
createdAt: now,
updatedAt: now,
isDeleted: false,
};
// Step 4: Delegate to repository
await this.repository.create(record);
return record;
}
}
4. Repository (Infrastructure Layer)
import { db } from '../database/database';
export class RecordRepository {
async create(record: Record) {
// Step 4a: Execute SQL INSERT
await db.runAsync(
`INSERT INTO records (
id, title, subtitle, metadata, type, userId,
createdAt, updatedAt, isDeleted
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[
record.id,
record.title,
record.subtitle ?? null,
record.metadata ?? null,
record.type,
record.userId ?? null,
record.createdAt,
record.updatedAt,
record.isDeleted ? 1 : 0,
]
);
}
}
5. Database (Infrastructure Layer)
import * as SQLite from "expo-sqlite";
// Step 4b: SQLite executes the INSERT
export const db = SQLite.openDatabaseSync("simple_manager.db");
Error Handling Flow
Errors propagate upward through the layers:
// 1. Database error occurs
db.runAsync(...) // Throws: "UNIQUE constraint failed"
// 2. Repository lets it bubble up
async create(record: Record) {
await db.runAsync(...); // Error propagates
}
// 3. Service might transform it
async create(title: string, type: string) {
try {
await this.repository.create(record);
} catch (error) {
// Could log or transform error here
throw error;
}
}
// 4. Hook catches and handles
const create = async (title: string, type: string) => {
try {
await service.create(title, type);
await load();
} catch (error) {
throw new Error(getErrorMessage(error));
}
};
// 5. UI displays to user
const handleCreate = async () => {
try {
await create(title, 'client');
} catch (error) {
alert(error.message); // User sees friendly message
}
};
State Management
Local Component State
- Form inputs
- Loading states
- Modal visibility
- Temporary UI state
Hook State
- Fetched data (records list)
- Loading indicators
- Error states
Global State (Zustand)
- User session
- App-wide settings
- Theme preferences
Database State (Source of Truth)
- Persisted records
- User data
- Application data
The database is always the single source of truth. UI state is ephemeral and rebuilt from database on app restart.
Data Refresh Strategy
The application uses a simple refresh strategy:
- After any mutation (create, update, delete)
- Reload all data from the database
- Update component state
- Trigger re-render
const create = async (title: string, type: string) => {
await service.create(title, type);
await load(); // Always refresh after mutation
};
const update = async (record: Record) => {
await service.update(record);
await load(); // Always refresh after mutation
};
const remove = async (id: string) => {
await service.delete(id);
await load(); // Always refresh after mutation
};
This strategy is simple and reliable. For larger datasets, consider implementing optimistic updates or pagination.
Future: API Integration
When migrating to an API backend, the data flow remains similar:
Only the Infrastructure Layer needs to change. Services, hooks, and UI remain unchanged.
Related Pages
Clean Architecture
Understand the architectural layers
Folder Structure
See where each piece lives