| name | linear-reference-architecture |
| description | Production-grade Linear integration architecture patterns.
Use when designing system architecture, planning integrations,
or reviewing architectural decisions.
Trigger with phrases like "linear architecture", "linear system design",
"linear integration patterns", "linear best practices architecture".
|
| allowed-tools | Read, Write, Edit, Grep |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
Linear Reference Architecture
Overview
Production-grade architectural patterns for Linear integrations.
Prerequisites
- Understanding of distributed systems
- Experience with cloud infrastructure
- Familiarity with event-driven architecture
Architecture Patterns
Pattern 1: Simple Integration
Best for: Small teams, single applications
┌─────────────────────────────────────────────────────────┐
│ Your Application │
├─────────────────────────────────────────────────────────┤
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Linear SDK │ │ Cache Layer │ │ Webhook │ │
│ │ (API calls) │ │ (In-memory) │ │ Handler │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────┐
│ Linear API │
│ api.linear.app │
└──────────────────────┘
import { LinearClient } from "@linear/sdk";
const cache = new Map<string, { data: any; expires: number }>();
export class SimpleLinearService {
private client: LinearClient;
constructor() {
this.client = new LinearClient({
apiKey: process.env.LINEAR_API_KEY!,
});
}
async getWithCache<T>(key: string, fetcher: () => Promise<T>, ttl = 300): Promise<T> {
const cached = cache.get(key);
if (cached && cached.expires > Date.now()) {
return cached.data;
}
const data = await fetcher();
cache.set(key, { data, expires: Date.() + ttl * });
data;
}
() {
.(, ..());
}
}
Pattern 2: Service-Oriented Architecture
Best for: Medium teams, multiple applications
┌────────────────────────────────────────────────────────────────┐
│ API Gateway │
└────────────────────────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐
│ Issues Service │ │ Projects Service│ │ Notifications Svc │
│ (CRUD + sync) │ │ (Planning) │ │ (Slack, Email) │
└─────────────────┘ └─────────────────┘ └─────────────────────┘
│ │ │
└────────────────────┼────────────────────┘
▼
┌─────────────────┐
│ Linear Gateway │
│ (Rate limiting, │
│ caching, auth) │
└─────────────────┘
│
▼
┌─────────────────┐
│ Linear API │
└─────────────────┘
import { LinearClient } from "@linear/sdk";
import Redis from "ioredis";
export class LinearGateway {
private client: LinearClient;
private redis: Redis;
private rateLimiter: RateLimiter;
constructor() {
this.client = new LinearClient({ apiKey: process.env.LINEAR_API_KEY! });
this.redis = new Redis(process.env.REDIS_URL);
this.rateLimiter = new RateLimiter({
maxRequests: 1000,
windowMs: 60000,
});
}
async execute<T>(operation: string, fn: () => Promise<T>): Promise<T> {
cacheKey = ;
cached = ..(cacheKey);
(cached) .(cached);
..();
start = .();
{
result = ();
..(cacheKey, , .(result));
metrics..(.() - start);
result;
} (error) {
metrics..({ operation });
error;
}
}
}
Pattern 3: Event-Driven Architecture
Best for: Large teams, real-time requirements
┌─────────────────────────────────────────────────────────────────────┐
│ Event Bus (Kafka/SQS) │
└─────────────────────────────────────────────────────────────────────┘
▲ │ │
│ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Webhook Ingester│ │ Event Processor │ │ Notification │
│ (Linear events) │ │ (Business logic)│ │ Service │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│
▼
┌─────────────────┐
│ State Store │
│ (PostgreSQL) │
└─────────────────┘
│
▼
┌─────────────────┐
│ Linear Sync │
│ (Outbound) │
└─────────────────┘
│
▼
┌─────────────────┐
│ Linear API │
└─────────────────┘
import { Kafka } from "kafkajs";
const kafka = new Kafka({
brokers: [process.env.KAFKA_BROKER!],
});
const producer = kafka.producer();
export async function ingestWebhook(event: LinearWebhookEvent): Promise<void> {
if (!verifySignature(event)) {
throw new Error("Invalid signature");
}
await producer.send({
topic: `linear.${event.type.toLowerCase()}`,
messages: [{
key: event.data.id,
value: JSON.stringify(event),
headers: {
action: event.action,
timestamp: event.webhookTimestamp.toString(),
},
}],
});
}
const consumer = kafka.({ : });
consumer.({ : [, ] });
consumer.({
: ({ topic, message }) => {
event = .(message.!.());
(topic) {
:
(event);
;
:
(event);
;
}
},
});
Pattern 4: CQRS with Event Sourcing
Best for: Complex domains, audit requirements
┌─────────────────────────────────────────────────────────────────────┐
│ Command Side │
├─────────────────────────────────────────────────────────────────────┤
│ Commands → Command Handler → Event Store → Event Publisher │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ Query Side │
├─────────────────────────────────────────────────────────────────────┤
│ Event Subscriber → Projector → Read Models → Query API │
└─────────────────────────────────────────────────────────────────────┘
interface StoredEvent {
id: string;
aggregateId: string;
aggregateType: string;
eventType: string;
data: Record<string, unknown>;
metadata: {
userId: string;
correlationId: string;
causationId: string;
timestamp: Date;
};
version: number;
}
class EventStore {
async append(aggregateId: string, events: StoredEvent[]): Promise<void> {
await db.transaction(async (tx) => {
for (const event of events) {
await tx.insert(eventsTable).values(event);
}
});
for (const event of events) {
await eventBus.publish(event);
}
}
(: ): <[]> {
db.().(eventsTable)
.((eventsTable., aggregateId))
.(eventsTable.);
}
}
{
()
(: ): <> {
db.(issueReadModel).({
: event.,
...event.,
: event..,
});
}
()
(: ): <> {
db.(issueReadModel)
.(event.)
.((issueReadModel., event.));
}
}
Project Structure
linear-integration/
├── src/
│ ├── api/ # REST/GraphQL API
│ │ ├── routes/
│ │ └── middleware/
│ ├── services/ # Business logic
│ │ ├── issue-service.ts
│ │ ├── project-service.ts
│ │ └── sync-service.ts
│ ├── infrastructure/ # External integrations
│ │ ├── linear/
│ │ │ ├── client.ts
│ │ │ ├── cache.ts
│ │ │ └── webhook-handler.ts
│ │ ├── database/
│ │ └── cache/
│ ├── domain/ # Domain models
│ │ ├── issue.ts
│ │ └── project.ts
│ └── config/ # Configuration
│ └── index.ts
├── tests/
│ ├── unit/
│ ├── integration/
│ └── e2e/
└── infrastructure/ # IaC
├── terraform/
└── kubernetes/
Resources
Next Steps
Configure multi-environment setup with linear-multi-env-setup.