Queryable Projection

Searchable data and PHI do not have to live in the same store.

Field-level encryption for PII was the easy half: custom JPA converters, transparent to the domain layer. The hard half is that encryption destroys search, and search was a core product surface.

The obvious fix is to make the ciphertext itself searchable, storing hashed substrings in a parallel column so the database can run indexed lookups against hashes. I rejected it on two grounds. Hashed tokens over a name distribution are guessable by frequency analysis, and the scheme threads derived PHI through the primary store's query path, which is exactly where I wanted less of it. Decrypt-and-scan in application memory fails differently: it does not scale past small result sets, and it widens the plaintext blast radius to every process that runs a query.

What I built instead separates the searchable projection from the PHI store. The index holds surrogate keys and the minimum set of searchable fields. PHI lives encrypted elsewhere. A query returns IDs, and the application re-hydrates only the records the caller is authorized to see, through the Link Service. That buys a real search engine with its full feature set while keeping the bulk of PHI outside the search path entirely.

Two choices matter more than the mechanism. Which fields enter the projection is a call for the data owners, not for me, so the boundary is configurable rather than hardcoded to my judgment. And free text is excluded on principle: notes and similar fields are never indexed, because a leaked search index of clinical free text is a liability I refuse to own. The residual risk is real and accepted. The projection is still a second copy of whatever it holds, so I kept it as small as the product would tolerate and put authorization on re-hydration rather than trusting the query to be the gate.

Encryption is rarely the hard part. The design work is deciding what is allowed to leave the encrypted store.