Software Architecture and Philosophical Anthropology
Clean software architecture and the accidental mirror of the architecture of the soul
As I’ve traced in previous posts, a philosophical anthropology helps provide an interpretive grid for LLMs as an understanding of the architecture of a human person provides a point of comparison and contrast with language models. Now, another application of this philosophical-technical connection may be in software architecture. Perhaps LLMs are effectively precisely because they do imitate the structures of the soul (and the cosmos).
Imagine we decided to design software with the explicit aim at replicating the architecture of the soul—how would that impact the quality of our software?
Just as humans have external senses, perhaps we may have a use case where my software needs to receive data from the outside world:
const users = [...]
app.get('/users', (req, res) => {
res.json({ users })
})Now, just as the modalities of the various external senses are integrated into unified phantasm via the common sense, perhaps we may want to integrate multiple inputs from external sources into a unified event:
import {
ReceiveMessageCommand,
DeleteMessageCommand,
SQSClient,
DeleteMessageBatchCommand,
} from "@aws-sdk/client-sqs";
const client = new SQSClient({});
const SQS_QUEUE_URL = "queue_url";
const receiveMessage = (queueUrl) =>
client.send(
new ReceiveMessageCommand({
AttributeNames: ["SentTimestamp"],
MaxNumberOfMessages: 10,
MessageAttributeNames: ["All"],
QueueUrl: queueUrl,
WaitTimeSeconds: 20,
VisibilityTimeout: 20,
}),
);
export const main = async (queueUrl = SQS_QUEUE_URL) => {
const { Messages } = await receiveMessage(queueUrl);
if (!Messages) {
return;
}
if (Messages.length === 1) {
console.log(Messages[0].Body);
await client.send(
new DeleteMessageCommand({
QueueUrl: queueUrl,
ReceiptHandle: Messages[0].ReceiptHandle,
}),
);
} else {
await client.send(
new DeleteMessageBatchCommand({
QueueUrl: queueUrl,
Entries: Messages.map((message) => ({
Id: message.MessageId,
ReceiptHandle: message.ReceiptHandle,
})),
}),
);
}
};
// In practice, this is what message queues do:
// - AWS SQS, RabbitMQ, Kafka
// - Multiple producers send specialized messages
// - Queue integrates and distributes to consumersThen, just as the common sense distributes the phantasm, so to does the above message queue distribute the messages it aggregates from various sources.
Next, we may want to receive this message and persist its content—just as the imagination persists the distributed phantasm. Moreover, we want to ensure this data can be updated over time:
class Store {
state = new Map();
store(event) {
this.state.set(event.id, {
data: event.data,
})
}
retrieve(id) {
return this.state.get(id)
}
}
// Why this mirrors imagination:
// - Holds representations that persist
// - Available for retrieval/manipulation
// - Atemporal (no "when" stored, just "what")
// - Can be updated without touching original source
//
// In practice: Redis, Memcached, application stateAnd like the estimative power, we may want to recognize patterns in stored representation:
class PatternEngine {
perceiveIntention(transaction) {
if (this.matchesPattern(transaction, 'fraudulent')) {
return { intention: 'block', urgency: 'high' }
}
if (this.matchesPattern(transaction, 'unusual')) {
return { intention: 'review', urgency: 'medium' }
}
return { intention: 'approve', urgency: 'low' }
}
matchesPattern(data, pattern) {
// ML model recognizes patterns in particular cases
// Not universal truth, but practical judgment
return this.model.predict(data) === pattern
}
}
// Why this mirrors estimative power:
// - Operates on stored representations (from imagination)
// - Recognizes practical meanings ("suspicious", "safe")
// - Particular judgments, not universal truths
// - Informs action (but doesn't decide)
//
// In practice: Fraud detection, anomaly detection, classification servicesFinally, we likely want to store events with temporal/intentional context in memory:
class MemoryStore {
async record(event, intention, context) {
// Store not just "what" but "when" and "why"
await db.event.create({
data: {
data: event.data,
createdAt: new Date(),
location: context.location,
prediction: intention.prediction,
urgency: intention.urgency,
relatedEvents: context.links
}
})
}
async recall(criteria) {
// Query by time AND significance
return await db.event.findMany({
where: {
when: {
gte: criteria.startDate, // "After yesterday"
lte: criteria.endDate // "Before today"
},
why: criteria.intention // "Show me blocked transactions"
},
orderBy: { when: 'desc' } // Temporal ordering
})
}
}
// Why this mirrors memory:
// - Stores not just "what" but "when" (temporal)
// - Includes "why" (intentional significance)
// - Can query by time: "What happened yesterday?"
// - Can query by meaning: "Show me suspicious events"
// - Past preserved with full context
//
// In practice: Event sourcing, audit logs, transaction history
// Tools: PostgreSQL with timestamps, event stores, append-only logsoint: Software design patterns accidentally mirror the structures of the soul.Good architects discovered these patterns not by reading Aquinas, but by solving real problems. They found that:
Multiple inputs need integration (common sense)
Representations need storage (imagination)
Patterns need recognition (estimative power)
History needs preservation (memory)
These patterns work because they reflect the ontological structure of reality itself. Different kinds of operations require different kinds of layers—not principally for organizational tidiness, but because reality too has boundaries/separations of concerns. The medieval philosophers knew this through theoria (contemplation of reality), while engineers rediscovered it through techne (how to make human artifacts function properly).
What if we ascended to the realm of theoria and contemplate how good software architecture is made good by participating in structures of reality that transcend software engineering as such?
What if we then descended back to techne, and designed software with reality’s structure in mind from the start (as opposed to from the starting point of technical jargon)?
When you architecting a system, you're not just organizing tools/systems and their code. Good architecture is mirroring the powers of the soul, and the metaphysical structures that these powers participate in. We can call them system layers and service boundaries, etc., but they point to a broader architecture of reality.
Read Next:


