How Domain-Driven Design Helped Us Prevent PHI Leaks in Production Logs
- July 20, 2025
- Posted by: Brett Knapik
- Category: Software Architecture

While building a Class III medical device for processing pharmacy orders, we found ourselves dealing with sensitive patient data—information like medication history and Medical Record Numbers (MRNs). The system had to meet strict regulatory standards, including HIPAA compliance, and needed to be both observable and secure.
Then we discovered a problem that hit both our compliance goals and our engineering confidence: Protected Health Information (PHI) was leaking into our logs.
This wasn’t the result of negligence or carelessness. It was happening implicitly, through something as simple as a ToString() call. When developers logged domain values directly like
logger.LogInformation($"Processing order for {mrn}");
the raw MRN would show up in the logs. And since MRNs were just strings at the time, there was no safeguard in place to prevent that.
If those logs had ever been exposed, it could have resulted in a major HIPAA violation, including financial penalties and serious reputational damage.
A Shift Toward Domain-Driven Design
This incident prompted a fundamental shift in how we thought about modeling data in our system. We adopted Domain-Driven Design (DDD) principles and began introducing value objects as a way to make sensitive domain data more explicit, intentional, and safe.
We created a custom ValueObject base class that became the foundation for every type representing PHI. The key change was overriding the ToString() method so that all value objects would return “Redacted” by default. This ensured that even if a sensitive object was accidentally logged, it would never expose PHI.
In addition to the default ToString(), we introduced two explicit methods: ToPhiString(), which returned the original value, and ToPhiSafeString(), which returned a safe representation suitable for display or partial logging.
Here’s a simplified version of what that looked like in code:
public abstract class ValueObject
{
public override string ToString() => "Redacted";
public virtual string ToPhiString()
=> throw new NotImplementedException("Override in derived class");
public virtual string ToPhiSafeString() => "Redacted";
}
public class MedicalRecordNumber : ValueObject
{
private readonly string _value;
public MedicalRecordNumber(string value)
{
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentException("MRN cannot be null or empty", nameof(value));
_value = value;
}
public override string ToPhiString() => _value;
public override string ToPhiSafeString() => $"MRN-{_value[^4..]}";
}
With this design in place, logging a value like logger.LogInformation($”MRN: {mrn}”) would output MRN: Redacted. To log the actual value, a developer would need to make an explicit call to mrn.ToPhiString(), which could easily be spotted during a code review or audit.
Why it Worked
This approach had several benefits. First, it immediately reduced the risk of accidental PHI exposure in our logs. Second, it made any use of PHI highly visible in the codebase. Third, it helped with HIPAA compliance by establishing a clear, testable boundary around sensitive data.
We also implemented unit tests to verify that all value objects adhered to this behavior. These tests provided further assurance that our logging remained safe even as the system evolved.
Perhaps most importantly, this change created a mindset shift within the team. By adopting a standard base class for value objects, we gave engineers confidence that logging a domain object wouldn’t inadvertently expose sensitive data. Developers were encouraged to think critically about how and where PHI was used—and that mindset carried over into other parts of the system.
Beyond Elegance: Design as Guardrail
Domain-Driven Design is often celebrated for its elegance and clarity. But in this case, it did something more important: it created meaningful friction. It forced engineers to stop and think before handling PHI. It embedded safety directly into the model.
We didn’t adopt value objects because they were trendy or clean. We adopted them because they gave us control over how sensitive data was handled—and because they helped us meet both our engineering goals and our compliance obligations.
If you’re working in a regulated industry—or even if you just want better visibility and control over your domain—it’s worth considering how DDD can help you model not just behavior, but safety.
This article originally appeared on LinkedIn at https://www.linkedin.com/pulse/how-domain-driven-design-helped-us-prevent-phi-leaks-logs-knapik-yaune