Start HereOrdered learning track

Ownership Models

Learn Java Microservices File Handling, State, Configuration and Secret Management - Part 005

Ownership model untuk file, state, configuration, dan secret di Java microservices agar lifecycle, akses, audit, dan failure handling tidak kabur.

13 min read2556 words
PrevNext
Lesson 0570 lesson track01–13 Start Here
#java#microservices#ownership#file-handling+3 more

Part 005 — Ownership Models

Production system rarely fails because nobody wrote code.

It fails because nobody clearly owns the runtime thing the code depends on.

Di microservices, banyak incident tidak dimulai dari algoritma yang salah. Incident sering dimulai dari hal yang terlihat “operasional”: bucket object storage dipakai beberapa service tanpa kontrak, config diubah tanpa owner, secret dirotasi tanpa kesiapan consumer, cache dianggap optimization padahal memengaruhi authorization, atau file metadata menunjuk object yang sudah dihapus job retention.

Masalahnya bukan sekadar dokumentasi kurang. Masalahnya adalah ownership model tidak eksplisit.

Di part ini kita membangun model kepemilikan untuk empat kategori runtime artifact:

  1. File — binary object, metadata, evidence, attachment, generated report, export.
  2. State — durable state, ephemeral state, workflow state, cache, derived state.
  3. Configuration — nilai yang mengubah behavior service tanpa rebuild.
  4. Secret — credential, token, certificate, key, private material.

Tujuannya bukan membuat organisasi terlihat rapi. Tujuannya agar service Java bisa didesain dengan boundary yang jelas: siapa boleh membuat, mengubah, membaca, menghapus, merotasi, merecover, dan membuktikan runtime artifact.


1. Mental Model: Artifact Selalu Punya Owner

Ownership berarti:

For every runtime artifact, there must be an accountable boundary
for semantic meaning, lifecycle, access, integrity, audit, and recovery.

Setiap artifact harus punya jawaban jelas untuk pertanyaan berikut:

PertanyaanMakna Praktis
Siapa yang boleh membuatnya?Producer authority
Siapa yang boleh membacanya?Access boundary
Siapa yang boleh mengubahnya?Mutation authority
Siapa yang boleh menghapusnya?Lifecycle authority
Siapa yang menjelaskan maknanya?Semantic owner
Siapa yang menjaga availability-nya?Operational owner
Siapa yang bertanggung jawab jika bocor?Security owner
Siapa yang membuktikan riwayatnya?Audit/compliance owner
Siapa yang memulihkan saat rusak?Recovery owner

Tanpa model ini, service akan tumbuh seperti shared filesystem: semua bisa menulis, semua bisa membaca, tidak ada yang tahu mana sumber kebenaran.


2. Ownership Bukan Storage Location

Kesalahan umum:

“File ada di bucket A, berarti tim platform pemilik file itu.”

Tidak selalu.

Storage location adalah physical custody. Ownership adalah semantic authority.

Contoh:

Bucket: regulator-prod-evidence
Storage platform owner: Platform/SRE
Evidence semantic owner: Enforcement Case Service
Retention policy owner: Legal/Compliance
Encryption key owner: Security Platform
Access grant owner: Case Access Control Service
Audit consumer: Investigation/Audit Team

Satu file bisa melewati banyak boundary kepemilikan. Yang berbahaya adalah menyamakan semuanya menjadi satu label “owner”.

Gunakan pemisahan ini:

Ownership TypePertanyaan
Semantic ownerApa arti artifact ini di domain?
Physical ownerDi mana artifact disimpan dan siapa menjaga storage?
Lifecycle ownerKapan dibuat, dipromosikan, diarsipkan, dihapus?
Access ownerSiapa boleh membaca/menulis?
Integrity ownerSiapa memastikan checksum, version, immutability?
Cost ownerSiapa bertanggung jawab atas storage, egress, compute cost?
Incident ownerSiapa memimpin mitigasi saat gagal?

Production design yang baik memisahkan semantic ownership dari physical custody.


3. Ownership Map

Service boleh memakai storage yang dimiliki platform, tetapi semantic lifecycle tetap harus dimiliki bounded context. Secret boleh berasal dari Vault atau cloud secret manager, tetapi consumer service tetap bertanggung jawab atas cara secret dipakai, dicache, direload, dan tidak dibocorkan. Config boleh dikelola GitOps, tetapi service/domain team tetap bertanggung jawab terhadap validitas behavior akibat config tersebut.


4. Ownership untuk File

File production-grade jarang hanya “file”. Biasanya ia adalah:

Binary payload + metadata + lifecycle state + access policy + audit trail

Contoh model metadata:

public record StoredFile(
    String fileId,
    String storageKey,
    String bucket,
    String contentType,
    long sizeBytes,
    String sha256,
    FileLifecycleStatus status,
    String ownerService,
    String ownerDomain,
    String createdBy,
    Instant createdAt,
    Instant retentionUntil
) {}

Lifecycle state:

public enum FileLifecycleStatus {
    UPLOADING,
    UPLOADED,
    QUARANTINED,
    SCANNED,
    ACCEPTED,
    REJECTED,
    ARCHIVED,
    DELETION_REQUESTED,
    DELETED
}

Field ownerService dan ownerDomain bukan kosmetik. Keduanya dipakai untuk authorization, audit, retention, debugging, cleanup, migration, data lineage, dan incident response.

4.1 Semantic Owner

Semantic owner menjawab:

  • apakah file ini bukti kasus?
  • apakah ini export sementara?
  • apakah ini generated PDF?
  • apakah ini raw upload yang belum dipercaya?
  • apakah ini dokumen final yang tidak boleh berubah?

Contoh:

File TypeSemantic Owner
Evidence photoCase Evidence Service
Payment receipt uploadPayment Service
User avatarProfile Service
Generated monthly reportReporting Service
Investigation packageInvestigation Service

4.2 Physical Owner

Physical owner menjaga storage layer:

  • bucket/container;
  • lifecycle policy;
  • storage class;
  • encryption setting;
  • replication;
  • backup;
  • monitoring;
  • quota;
  • egress policy.

Biasanya ini platform team atau storage platform owner. Namun physical owner tidak boleh menentukan sendiri apakah evidence boleh dihapus. Itu domain/lifecycle owner.

4.3 Lifecycle Owner

Lifecycle owner menjawab:

  • kapan file dianggap committed?
  • kapan file masuk quarantine?
  • kapan file boleh dihapus?
  • apakah file harus legal hold?
  • apakah delete berarti hard delete, soft delete, tombstone, atau crypto-shred?

Invariant:

A file referenced by an active regulatory case must not be physically deleted
unless the case lifecycle, retention rule, and legal hold state allow it.

4.4 Access Owner

Access owner menentukan siapa boleh membaca metadata dan payload. Metadata access dan payload access harus dipisah. User bisa melihat bahwa dokumen ada, tetapi belum tentu boleh mengunduh binary payload.

public interface FileAccessPolicy {
    boolean canReadFilePayload(UserContext user, StoredFile file);
    boolean canReadFileMetadata(UserContext user, StoredFile file);
    boolean canDeleteFile(UserContext user, StoredFile file);
}

Untuk sistem sensitif, payload access biasanya lebih ketat daripada metadata access.


5. Ownership untuk State

State adalah semua hal yang memengaruhi keputusan service dari waktu ke waktu.

State TypeContohOwnership Risk
Durable statePostgreSQL row, event stream, object metadataSalah owner menyebabkan corruption
Ephemeral statetemp file, in-memory map, pod local diskHilang saat restart
Derived statesearch index, cache projectionBisa stale
Workflow statecase status, saga state, BPMN process stateSalah transisi merusak lifecycle
Session statelogin session, CSRF token, wizard progressCross-device inconsistency
Operational stateleader lock, job checkpointDuplicate processing

Mental model:

A service may be stateless in compute,
but the product is never stateless.
State always exists somewhere.

Pertanyaan yang benar bukan “service ini stateless atau tidak”, melainkan:

Where is the state?
Who owns it?
Can it be rebuilt?
What happens when it is stale, missing, duplicated, or corrupted?

5.1 Durable State Owner

Durable state owner adalah bounded context yang punya hak mengubah state kebenaran.

Case Service owns Case.status.
Evidence Service owns EvidenceFile.lifecycleStatus.
Access Control Service owns PermissionGrant.
Notification Service does not own Case.status even if it sends notifications.

Jangan biarkan consumer service mengubah state milik domain lain lewat shared database.

Buruk:

Reporting Service updates case.status directly because it has DB access.

Lebih baik:

Reporting Service emits reportCompleted event.
Case Service decides whether case.status can transition.

5.2 Cache Owner

Cache sering dianggap tidak punya owner karena “bisa dibuang”. Itu salah. Cache tetap butuh owner karena cache punya TTL, invalidation policy, fallback behavior, memory budget, correctness impact, dan stampede protection.

Pertanyaan wajib:

If this cache returns stale value for 10 minutes, what invariant can break?

Jika jawabannya “authorization bisa salah”, cache itu bukan optimization biasa. Ia security-sensitive state.

5.3 Workflow State Owner

Workflow state punya risiko tinggi karena biasanya mengikat banyak entity.

DRAFT -> SUBMITTED -> UNDER_REVIEW -> ESCALATED -> ENFORCEMENT_ACTION -> CLOSED

Contoh rule lintas file dan case:

Evidence file can be attached only when case is DRAFT or UNDER_REVIEW.
Evidence file cannot be physically deleted after case reaches ENFORCEMENT_ACTION.

State owner harus memegang rule transisi, bukan UI, bukan worker, bukan storage adapter.


6. Ownership untuk Configuration

Configuration adalah value yang mengubah behavior service tanpa mengubah build artifact. Spring Boot mendukung externalized configuration dari banyak sumber seperti properties/YAML, environment variable, command-line argument, dan config import. Kubernetes menyediakan ConfigMap untuk konfigurasi non-sensitive yang bisa diberikan ke workload sebagai environment variable atau mounted file.

Konfigurasi punya beberapa kategori owner.

Config TypeContohOwner Utama
Application defaultdefault timeout, default page sizeService team
Environment configDB host, broker URL, endpoint URLPlatform/SRE
Business tuningmax upload size, risk thresholdDomain/product owner + service team
Operational tuningthread pool, retry countService team + SRE
Security configallowed issuer, mTLS modeSecurity + service team
Feature flagnew flow enabled for cohortProduct/platform owner
Tenant configper-tenant retention daysDomain owner

Rule:

The owner of a configuration value is the party accountable
for the consequence of changing that value.

Contoh:

  • server.port konsekuensinya operasional → platform/service owner.
  • evidence.max-upload-size konsekuensinya UX, storage cost, abuse risk → product/domain/service.
  • case.retention-years konsekuensinya legal → compliance/domain.
  • auth.allowed-issuer konsekuensinya security → security/service.
  • worker.batch-size konsekuensinya throughput → service/SRE.

6.1 Config Harus Punya Schema

Config tanpa schema adalah hidden API.

@ConfigurationProperties(prefix = "evidence.file")
@Validated
public record EvidenceFileProperties(
    @Min(1) long maxUploadSizeMb,
    @NotBlank String quarantineBucket,
    @NotBlank String acceptedBucket,
    @NotNull Duration scanTimeout,
    boolean directUploadEnabled
) {}

Owner harus jelas di config catalog:

KeyOwnerChange ApprovalRuntime Reload?Risk
evidence.file.max-upload-size-mbEvidence ServiceService + ProductNoCost + abuse
evidence.file.quarantine-bucketPlatform + EvidencePlatform + ServiceNoData loss
evidence.file.scan-timeoutEvidence Service + SREServiceYesThroughput
evidence.file.direct-upload-enabledProduct + ServiceRelease ownerYesUX + security

7. Ownership untuk Secret

Secret bukan config biasa. Secret punya sensitivity, expiration, revocation, rotation, audit requirement, least privilege, blast radius, dan leakage impact.

Kubernetes Secret menyimpan data sensitif seperti password, token, atau key. Vault dynamic secrets memiliki lease dengan TTL; saat lease habis, consumer tidak boleh lagi menganggap secret valid.

Pisahkan empat peran:

RoleTanggung Jawab
Secret authoritySistem yang menerbitkan secret: Vault, AWS Secrets Manager, Azure Key Vault, GCP Secret Manager, PKI
Secret custodianPlatform yang menyimpan/mendistribusikan secret
Secret consumerService Java yang memakai secret
Secret risk ownerPihak yang menanggung dampak leakage/abuse

Contoh:

Database password for evidence-service:
- Authority: Vault database secrets engine
- Custodian: Vault platform team
- Consumer: evidence-service runtime
- Risk owner: Evidence domain + Security
- Operational owner: Evidence service + SRE

7.1 Secret Consumer Tetap Bertanggung Jawab

Jangan berkata:

Secret aman karena disimpan di Vault.

Vault hanya menyelesaikan sebagian problem. Service Java tetap harus tidak log secret, tidak expose secret via actuator/env endpoint, tidak menaruh secret di exception message, bisa refresh credential, bisa reconnect saat credential dirotasi, fail closed saat secret tidak valid, dan tidak cache secret melebihi TTL.

Gunakan tipe khusus untuk mengurangi accidental leak:

public final class SecretValue {
    private final String value;

    private SecretValue(String value) {
        if (value == null || value.isBlank()) {
            throw new IllegalArgumentException("Secret must not be blank");
        }
        this.value = value;
    }

    public static SecretValue of(String value) {
        return new SecretValue(value);
    }

    public String reveal() {
        return value;
    }

    @Override
    public String toString() {
        return "[REDACTED]";
    }
}

Wrapper ini tidak membuat memory aman sepenuhnya, tetapi mengurangi kesalahan umum seperti secret tercetak karena toString().


8. Ownership Matrix

ArtifactSemantic OwnerPhysical OwnerAccess OwnerLifecycle OwnerRecovery Owner
Uploaded file payloadDomain serviceStorage platformDomain access serviceDomain + complianceService + platform
File metadata rowDomain serviceDatabase platformDomain serviceDomain serviceService
Temp fileService runtimeContainer/nodeServiceServiceService
ConfigMapService/platformKubernetes platformRBAC/platformService/platformSRE
SecretSecurity/platformSecret managerIAM/RBAC/securitySecurity + serviceSecurity + service
Cache entryServiceCache platformServiceServiceService
Workflow stateDomain serviceDB/BPM platformDomain serviceDomain serviceService
Audit eventService/domainAudit platformAudit/securityComplianceAudit/platform

9. RACI untuk Runtime Artifact

ActivityService TeamPlatform/SRESecurityComplianceProduct/Domain
Define semantic meaningA/RCCCA/R
Provision storageCA/RCCI
Define access policyRCA/RCC
Define retentionCCCA/RA/R
Implement code integrationA/RCCIC
Monitor runtime healthRA/RCII
Rotate secretRC/RA/RII
Incident responseRRRCI
Audit evidenceCCCA/RC

Legend:

  • R — Responsible, melakukan pekerjaan.
  • A — Accountable, pemilik keputusan akhir.
  • C — Consulted.
  • I — Informed.

Jangan membuat semua orang A. Kalau semua accountable, tidak ada yang accountable.


10. Failure Modes

10.1 Shared Bucket Without Semantic Boundary

Gejala:

  • banyak service menulis ke bucket yang sama;
  • naming convention menjadi satu-satunya boundary;
  • tidak ada metadata registry;
  • cleanup job tidak tahu object mana milik siapa;
  • akses bucket terlalu luas.

Risiko:

  • accidental delete;
  • data leak;
  • retention violation;
  • orphan object;
  • undeclared dependency.

Solusi:

  • prefix per domain/service;
  • bucket policy least privilege;
  • metadata registry;
  • lifecycle state machine;
  • object tags;
  • ownership catalog.

10.2 Config Edited by Unknown Actor

Gejala:

  • config diubah langsung di cluster;
  • tidak ada PR;
  • tidak ada audit trail;
  • service restart dengan behavior berbeda;
  • rollback sulit.

Solusi:

  • config via GitOps;
  • required review per owner;
  • config schema validation;
  • startup validation;
  • runtime config diff endpoint yang aman;
  • alert on drift.

10.3 Secret Rotated Without Consumer Readiness

Gejala:

  • secret baru dibuat;
  • service masih memakai connection lama;
  • connection pool tidak refresh;
  • restart massal;
  • outage.

Solusi:

  • dual credential window;
  • connection pool max lifetime;
  • secret reload path;
  • canary rotation;
  • revocation after observation;
  • alert on old credential use.

10.4 Cache Treated as Non-State

Gejala:

  • cache dipakai untuk authorization;
  • TTL terlalu panjang;
  • invalidation tidak reliable;
  • stale value menyebabkan policy salah.

Solusi:

  • classify cache correctness impact;
  • use short TTL for sensitive state;
  • force recheck on critical action;
  • expose cache metrics;
  • support manual invalidation.

11. Ownership Decision Tree


12. Ownership Catalog

Untuk production-grade platform, buat catalog kecil. Tidak perlu tooling besar di awal. Bisa YAML/Markdown/DB table.

artifact: evidence-file-payload
type: file
semanticOwner: evidence-service
physicalOwner: storage-platform
accessOwner: case-access-service
lifecycleOwner: evidence-service
complianceOwner: legal-retention
securityOwner: security-platform
storage:
  provider: s3
  bucket: regulator-prod-evidence
  prefix: evidence/
integrity:
  checksum: sha256
  immutableAfterStatus: ACCEPTED
retention:
  defaultYears: 7
  legalHoldSupported: true
operations:
  recoveryRunbook: runbooks/evidence-file-recovery.md
  alertChannel: "#evidence-platform-alerts"

Catalog ini membantu menjawab:

  • apakah artifact masih aktif?
  • siapa yang perlu dihubungi saat incident?
  • bolehkah file dihapus?
  • siapa yang approve config change?
  • bagaimana rollback dilakukan?
  • apakah secret bisa dirotasi tanpa downtime?

13. Java Boundary Pattern

Jangan campur domain ownership dengan storage detail.

Buruk:

s3Client.deleteObject(bucketName, fileId);

Lebih baik:

public interface EvidenceFileLifecycleService {
    void requestDeletion(FileId fileId, UserContext actor);
}

Implementasi internal:

public final class EvidenceFileLifecycleServiceImpl implements EvidenceFileLifecycleService {
    private final EvidenceFileRepository repository;
    private final FileAccessPolicy accessPolicy;
    private final RetentionPolicy retentionPolicy;
    private final ObjectStorage storage;
    private final AuditLog auditLog;

    @Override
    public void requestDeletion(FileId fileId, UserContext actor) {
        StoredFile file = repository.getRequired(fileId);

        if (!accessPolicy.canDeleteFile(actor, file)) {
            throw new AccessDeniedException("Actor cannot delete file metadata or payload");
        }

        if (!retentionPolicy.canDelete(file)) {
            throw new RetentionViolationException("File is still under retention or legal hold");
        }

        repository.markDeletionRequested(fileId);
        auditLog.record("FILE_DELETION_REQUESTED", actor.id(), fileId.value());
    }
}

Perhatikan:

  • API domain tidak expose bucket/key sebagai decision point;
  • deletion dimulai dari lifecycle state, bukan langsung storage delete;
  • access dan retention dicek sebelum side effect;
  • audit dicatat.

Storage delete fisik bisa dilakukan worker terpisah dengan retry dan compensation.


14. Review Checklist

File

  • Apa semantic type file ini?
  • Apakah file raw, trusted, quarantined, accepted, atau archived?
  • Siapa boleh upload?
  • Siapa boleh download?
  • Apakah checksum wajib?
  • Apakah object key immutable?
  • Bagaimana cleanup partial upload?
  • Apakah retention/hold berlaku?
  • Apakah metadata dan payload bisa diverge?
  • Bagaimana recovery jika payload hilang tapi metadata ada?

State

  • State ini durable, ephemeral, derived, cache, atau workflow?
  • Siapa source of truth?
  • Apakah state bisa direkonstruksi?
  • Apa risiko stale/duplicate/missing/corrupt?
  • Apakah ada concurrency control?
  • Apakah transisi state punya invariant?
  • Apakah ada audit event?

Configuration

  • Siapa owner value ini?
  • Apa konsekuensi perubahan?
  • Apakah perubahan butuh restart?
  • Apakah config bisa reload runtime?
  • Apa default aman?
  • Apakah ada validation?
  • Apakah ada audit trail?
  • Apakah config ini sebenarnya secret?

Secret

  • Siapa authority?
  • Siapa consumer?
  • Apakah secret punya TTL?
  • Bagaimana rotation?
  • Bagaimana revocation?
  • Apakah service bisa refresh?
  • Apakah secret bisa bocor via log/metrics/error?
  • Apakah access least privilege?

15. Key Takeaways

Ownership model adalah cara mencegah runtime artifact berubah menjadi liability.

Prinsip utamanya:

  1. Storage location is not ownership.
  2. Every artifact needs semantic owner, lifecycle owner, access owner, and recovery owner.
  3. File is not only bytes; it is bytes plus metadata, lifecycle, access policy, and audit trail.
  4. State always exists somewhere, even in “stateless” services.
  5. Configuration owner is the party accountable for the consequence of change.
  6. Secret manager does not remove consumer responsibility.
  7. Ownership must appear in architecture, code boundary, config catalog, runbook, and audit trail.

Di part berikutnya, ownership ini akan diturunkan menjadi production invariants: aturan yang harus selalu benar di runtime agar service tetap aman, recoverable, dan defensible.


References

Lesson Recap

You just completed lesson 05 in start here. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.

Continue The Track

Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.