
Command Palette
Search for a command to run...

Comments (1)
Join the discussionThe interface belongs to the application layer as your application needs to know it can generate a document/save the invoice somewhere.
The implementation belongs to the infrastructure layer as this deals with external libraries and the filesystem.
The basic idea is that your application logic (generating an invoice in this scenario) should not be directly tied to things that your app does not control or to separate things that change on a different timeframe.
Let me clarify: writing to the file system does not change very often, so create something that can accept the format you want to save. As this implementation will not change very often, you won't need to worry about writing to the file system anymore for a long time.
Then comes generating a pdf, this might change more often as you might want to update the library/plugin that creates the pdf. I've switched since writing this article from HTML > PDF generation to using the QuestPDF nuget. So this changes more than the filesystem writer, but less than the invoice generation.
Also, this knows about the invoice, but the invoice does not need to know about the format it'll be saved in. So that is another reason why to put this in another class/place. Since it prepares the invoice to be written to the file system, I think it's appropriate to place these two implementations together in the infrastructure layer.
Lastly, the invoice generation has changed the most over the years as this contains the most bugs (relevant to the other functionality), needed the most changes as I discovered more use cases. Since I split it up fairly nicely, I could make easy changes to the invoice generation without impacting the other implementations (too much).
Remember that downstream modules (invoice flows to pdf generation which flows to file writer) might need to change when upstream modules change, but the inverse should not be true.
P.S. I might have gone overboard with answering your question. I think I should start blogging again to get some thoughts off of my chest.
More from this blog
Closing thoughts on A-Frame architecture
Phew, that was a lot. I'm always surprised how simple solutions contain so much detail and nuance when I try to explain them. I hope I conveyed the message that A-Frame simplifies code within a module or class by separating the infrastructure compone...

Testing A-Frame architecture
No architecture is complete without an easy way to test the functionality. My recommended strategy is to use two types of tests: unit and integration. As far as test setup goes, there is no clear winner for a test framework. xUnit.NET, NUnit and the ...

Tackling complex examples using A-Frame architecture and Wolverine
The examples in the previous posts seem really nice for simple scenarios. How do I approach more advanced use cases? The big scenarios are: I need multiple pieces of data from different sources I need to perform an infrastructure call in the middle...

Leverage Wolverine's A-Frame architecture support
I’ve shown how I write code using the A-Frame architecture without help. Yet I find a library or framework very convenient when using advanced techniques. Wolverine is at its core a messaging framework, but goes well beyond that. It has an in-memory ...

A simple A-Frame example
Now the expected structure is clear, let's take a look at the code. I will start with a minimal API implementation to demonstrate that there is no need for a framework to implement this architecture. I do know of a framework that makes A-Frame effort...
