本文へ移動
cccskills
無料GitHub で公開

appfolio-reference-architecture

Reference architecture for AppFolio property management integration. Trigger: "appfolio architecture".

インストール方法を見る

含まれるファイル(1)

  • SKILL.md7.6 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

AppFolio Reference Architecture

Overview

Production architecture for property management integrations with the AppFolio Stack API. Designed for multi-property portfolios requiring real-time vacancy tracking, tenant lifecycle management, work order routing, and accounting reconciliation. Key design drivers: data freshness for leasing decisions, idempotent sync for financial accuracy, and tenant-facing portal responsiveness.

Prerequisites

  • A provider-verified contract for endpoints, authentication, events (if any), rate limits, data residency, and permitted downstream accounting/CRM effects.
  • Separate staging and production environments with managed secrets, synthetic fixtures, durable queues/idempotency stores, and named reconciliation owners.
  • Data classification that limits dashboard and cache models to the smallest permitted fields; tenant contact, payment, and balance data require separate encrypted stores and access controls.

Instructions

  1. Build the contract-bound client and safe-read service layer first, with endpoint budgets, minimized cache entries, and observability before adding write or event processing.
  2. Enable provider events only after the contract and durable raw-body, signature, persistence, and replay boundaries have been proven; otherwise use bounded incremental reconciliation.
  3. Persist an idempotency/reconciliation record before any work-order, accounting, tenant, or lease mutation and require explicit authorization.
  4. Validate the architecture in staging with synthetic data, a forced provider failure, duplicate event/retry, and rollback rehearsal before promotion.

Architecture Diagram

Dashboard (React) ──→ Property Service ──→ Redis Cache ──→ AppFolio Stack API
                           ↓                                 /properties
                      Queue (Bull) ──→ Sync Worker           /tenants
                           ↓                                 /leases
                      Event Handler ←── Provider events*      /work-orders
                           ↓                                 /bills
                      Accounting Sync ──→ QuickBooks/Xero

* Use provider events only when the active contract confirms their delivery and security semantics; otherwise feed the queue from bounded reconciliation.

Service Layer

class PropertyService {
  constructor(private client: AppFolioClient, private cache: CacheLayer) {}

  async getPortfolioSummary(propertyIds: string[]): Promise<PortfolioSummary> {
    const properties = await Promise.all(
      propertyIds.map(id => this.cache.getOrFetch(`prop:${id}`, () => this.client.get(`/properties/${id}`)))
    );
    return { totalUnits: properties.reduce((sum, p) => sum + p.units.length, 0),
             vacancyRate: this.calcVacancy(properties), pendingWorkOrders: await this.getPendingOrders(propertyIds) };
  }

  async routeWorkOrder(order: WorkOrderRequest): Promise<string> {
    const property = await this.client.get(`/properties/${order.propertyId}`);
    const vendor = this.selectVendor(property.region, order.category);
    return this.client.post('/work-orders', { ...order, assigned_vendor: vendor });
  }
}

Caching Strategy

const CACHE_CONFIG = {
  properties: { ttl: 300, prefix: 'prop' },     // 5 min — changes infrequently
  tenants:    { ttl: 120, prefix: 'tenant' },    // 2 min — moderate churn
  leases:     { ttl: 60,  prefix: 'lease' },     // 1 min — financial accuracy
  workOrders: { ttl: 30,  prefix: 'wo' },        // 30s — real-time tracking
  vacancies:  { ttl: 15,  prefix: 'vacancy' },   // 15s — leasing speed matters
};
// Webhook-driven invalidation: AppFolio events flush matching cache keys immediately

Event Pipeline

class PropertyEventPipeline {
  private queue = new Bull('appfolio-events', { redis: process.env.REDIS_URL });

  async onWebhook(event: AppFolioEvent): Promise<void> {
    await this.queue.add(event.type, event, { attempts: 3, backoff: { type: 'exponential', delay: 2000 } });
  }

  async processLeaseEvent(event: LeaseEvent): Promise<void> {
    if (event.type === 'lease.signed') await this.updateVacancy(event.propertyId);
    if (event.type === 'lease.terminated') await this.triggerMoveOutWorkflow(event);
  }

  async processWorkOrderEvent(event: WorkOrderEvent): Promise<void> {
    if (event.status === 'completed') await this.reconcileVendorInvoice(event);
  }
}

Data Model

interface Property { id: string; name: string; address: Address; units: Unit[]; region: string; }
interface TenantRef { id: string; leaseId: string; contactCiphertextRef: string; }
interface Lease    { id: string; propertyId: string; unitId: string; tenantId: string; startDate: string; endDate: string; monthlyRent: number; status: 'active' | 'pending' | 'terminated'; }
interface WorkOrder { id: string; propertyId: string; unitId: string; category: 'plumbing' | 'electrical' | 'hvac' | 'general'; status: string; assignedVendor: string; }

Scaling Considerations

  • Partition sync workers by property region to avoid cross-region API rate limits
  • Use read replicas for dashboard queries; write path goes through event pipeline
  • Batch tenant notifications (rent reminders, maintenance updates) via queue to avoid email rate limits
  • Cache vacancy data aggressively — leasing agents hit this endpoint 10x more than any other
  • Shard work order routing by property portfolio to enable independent scaling per management group

Error Handling

ComponentFailure ModeRecovery
Property syncAppFolio 429 rate limitExponential backoff with jitter, per-property circuit breaker
Lease webhookDuplicate event deliveryIdempotency key on lease ID + event timestamp
Work order routingVendor API timeoutQueue retry with fallback to manual assignment
Accounting syncBalance mismatchReconciliation queue with human review flag
Tenant portalCache miss stormStale-while-revalidate pattern, circuit breaker on API layer

Output

  • A contract-bound, staged architecture with service, cache, queue, data, and reconciliation ownership explicitly separated
  • Minimized dashboard references and encrypted/controlled boundaries for tenant contact, payment, and balance data
  • A deployment decision supported by staging failure, duplicate/replay, rate, rollback, and reconciliation evidence

Examples

For a vacancy-dashboard rollout, start with synthetic property and unit IDs, one bounded safe-read, and a cache that exposes data age. Inject a duplicate event or reconciliation record, a 429, and a downstream accounting timeout to prove the queue and idempotency store prevent duplicate effects. Promote only when results remain complete and authorized and the rollback/reconciliation owners can demonstrate their paths. If event support, data classification, durable state, or a write outcome is unverified, keep the corresponding path disabled and use operator-led reconciliation.

Resources

Next Steps

See appfolio-deploy-integration.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Build a synthetic-data CI gate for customer-controlled Abridge integration code without calling undocumented vendor interfaces. Use when adding regression checks to an Abridge deployment repository. Trigger with "test the Abridge integration".

日本語の概要は準備中です。原文の説明を表示しています。

jeremylongshore/tons-of-skills-marketplace2,8322026年10月13日 更新

Triage Abridge recording, note-review, Epic handoff, and access failures from observed evidence without inventing vendor error codes. Use when an Abridge clinical workflow is degraded. Trigger with "diagnose this Abridge issue".

日本語の概要は準備中です。原文の説明を表示しています。

jeremylongshore/tons-of-skills-marketplace2,8322026年10月13日 更新

Design and verify the clinician workflow from consent through recording, draft review, Linked Evidence, and final note disposition. Use when implementing or auditing an Abridge clinical documentation pathway. Trigger with "map the Abridge note workflow".

日本語の概要は準備中です。原文の説明を表示しています。

jeremylongshore/tons-of-skills-marketplace2,8322026年10月13日 更新

Verify the approved handoff of clinician-reviewed Abridge note sections into Epic templates and downstream workflows. Use when configuring or auditing Abridge-to-Epic documentation flow. Trigger with "verify the Abridge Epic handoff".

日本語の概要は準備中です。原文の説明を表示しています。

jeremylongshore/tons-of-skills-marketplace2,8322026年10月13日 更新

Evaluate Abridge adoption, workflow value, and avoidable operational friction without inventing prices or vendor billing meters. Use when reviewing an Abridge rollout or renewal. Trigger with "analyze Abridge value".

日本語の概要は準備中です。原文の説明を表示しています。

jeremylongshore/tons-of-skills-marketplace2,8322026年10月13日 更新

Produce a PHI-minimized Abridge support bundle with provenance, redaction, and protected escalation boundaries. Use when support needs reproducible evidence for an Abridge issue. Trigger with "build an Abridge debug bundle".

日本語の概要は準備中です。原文の説明を表示しています。

jeremylongshore/tons-of-skills-marketplace2,8322026年10月13日 更新

jeremylongshore のスキルをすべて見る

このスキルの問題を報告する