The 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.
