I'm building an application that is responsible for reading data from files and displaying them in charts. The whole application is responsible for manipulating data from files, which means I have to use different parts of the same object in almost every part of my project. Charts need actual data, list of opened files needs names and states of data, etc. On top of that my application uses MEF
, so others can add new functionality and modify current modules.
My biggest problem is how to separate that data, so each layer of my application has access only to part of that data that is required for it to work. What I mean by data is:
- Name of file
- Guid Id
List<IColumn>
data orbyte[]
data- Additional future variables
I decided to use across my application IDocument
. This is my custom representation of one file that can be read and displayed in chart. Each IDocument
must have an Id
and Name
because every computer file has a name. This has all the information that is needed for a simple list of files that were imported into the application. But it doesn't have the actual data that I want to display, so I use NoSQL database for storing IDocumentData
that has List<IColumn> Columns
that I can throw into a chart. IColumn
has only List<object> Rows
which basically mimics a table.
So I seperated my file into IDocument
and IDocumentData
, now how do I keep track of which data belongs to which document? I get IDocumentData
from IReader
module and I get IWizardData
from IFileImporterWizard
module.
I was thinking about creating some kind of builder that would assign the same Id to IDocument
and IWizardData
(contains importated Name
and Id
) and when I would want to access data I would pass Id
from IDocument
. Is this a good approach?
So for example:
public class DocumentBuilder {
private IDocumentFactory _documentFactory;
private IDocumentDataRepository _documentDataRepository;
public DocumentBuilder(IDocumentFactory documentFactory, IDocumentDataRepository documentDataRepository)
{
_documentFactory = documentFactory;
_documentDataRepository = documentDataRepository;
}
public IDocument Build(IWizardData wizardData, IDocumentData documentData)
{
var document = _documentFactory.Create();
document.Id = wizardData.Id;
documentData.Id = wizardData.Id;
_documentDataRepository.Add(documentData);
return document;
}
}
So my thinking looks like this: Open File wizard returns IWizardData
(name, filePath) -> IReader
returns IDocumentData
(List<IColumn>
columns) -> DocumentBuilder
builds IDocument
and saves data
EDIT:
So I figured out how to separate business and data layers by creating
interface IDocument : IDocumentData, IDocumentInformation
IDocumentInformation
has a name and other information. IDocumentData
has Id
and DataTable Data { get; set; }
. I can save IDocumentData
and IDocumentInformation
separately and still have Id
that is the same for both of them.
I decided to change IColumn
and List<object> Rows
, because I hope DataTable
will cause less problems. Although I'm still wondering if DataTable
is a good choice, because it will probably decrease performance when loading data. I thought that it would be a good idea to load from NoSQL
only a couple of records and then add them to chart and loop it until the end.
So now IFileWizard
returns IDocumentInformation
, IReader
takes IDocumentInformation
and returns IDocumentData
and then they get combined.
Naming these things takes some time, because they all use parts of the same object, but seeing IFileWizard
returning IDocumentInformation
is a little bit weird. On the other hand in order to make them easily interchangeable I would have to declare even more common interfaces and create interface IFileWizardResult:IInfo
and have interface IDocumentInformation:IInfo
and then return IFileWizardResult
and cast it to IInfo
, etc. I don't know if this is too much decoupling or not. I want this application to be easily scalable, but making a lot of interfaces not always means better decoupling.
-
Why is it so important that each layer can only access what they need and no more?Bart van Ingen Schenau– Bart van Ingen Schenau2016年12月10日 14:09:00 +00:00Commented Dec 10, 2016 at 14:09
-
2I think that it is a good practice to keep things separated, so it's more difficult to break application. So for example my chart won't be able to delete document, because it knows only about the data.FCin– FCin2016年12月10日 14:20:03 +00:00Commented Dec 10, 2016 at 14:20
-
1Separation of concern is good practice, in general.user255194– user2551942016年12月12日 15:43:35 +00:00Commented Dec 12, 2016 at 15:43
-
2I'm not sure I understand the question; do you want to have a single implementation which looks different to each layer? In that case you probably want to implement multiple interfaces (IFileInfo, IChartDataSource, IDeletableDocument,etc) on the same implementation and pass the object as that type of interface to the layer in question. Then each layer only sees a specific aspect of the underlying implementation. If it's something else you need, updating the question to be clearer would help - "Is this a good approach" is a very broad question :)Stephen Byrne– Stephen Byrne2016年12月12日 17:29:22 +00:00Commented Dec 12, 2016 at 17:29
-
@StephenByrne You might consider posting that as an answer.Adrian– Adrian2016年12月15日 19:55:49 +00:00Commented Dec 15, 2016 at 19:55
1 Answer 1
The answer to your question depends primarily on two factors:
The overall complexity of the application
To be extreme, if you are simply writing a script to process a bunch of files, you would probably be going overboard having multiple layers to your application in the first place.
On the other hand, if you are writing a full blown enterprise application, separating layers and data representation between these layers will guarantee long term maintainability and stability of the application.
There are many variations and trade offs between these layers.
The differences in representation needed by each layer
On one hand, you could have an application that needs JSON format all the way through, from client to data storage. If there are no future requirements predicted that might change that, you can use JSON on the client, process the JSON in the business layer and write the JSON to a file or a NoSQL database.
On the other hand, every layer might best work with a different format. Let's say your storage requirements might be binary files, so you would have to read that out on the data layer and convert it into sensible objects that can be processed by the business layer and then that business layer would process and summarize the data an turn it into charts. Just by the virtue of that it would make sense to have a different representation.
There are many variations in between, but in your case it sounds like the latter is the case, but if you have the choice you might also review storing your data in a format that is more useful to the other layers.
Notes on the Presentation Layer
In practice I have learned to pretty much always give the Presentation Layer it's own model, separate from what the datastore and the business layer use. This is for multiple reasons:
The presentation layer should not perform any business logic. However, if the model does not match what is to be shown you start finding funny pieces of business logic creeping into the presentation code.
Typesafe Languages like C# and Java are many times easier to refactor than templating languages and JavaScript. Thus if you tightly couple into such languages, you will loose much of the benefit of having used a typesafe language in the first place.
From what I have seen user interface technology still changes drastically, in the last 7 years I have gone from Flash/Flex to plain JS to jQuery, to Backbone to React. In other words the UI has seen many more rewrites than the backend code. Again, if you are loosely coupled this is much easier to deal with.
-
I thought about using a single data storage (NoSQL) and sharing it via Dependency Injection, this way I have less work to do and still have my decoupling. Also this way I can pass data from database to e.g. chart in 2-3 lines of code without any external classes and additional logic. Only repository and chart. Presentation is something that worries me more, because if I want to let people change the way things are displayed I have to expose properties to them in an easy to access way, and also store any changes that they make to styles.FCin– FCin2016年12月21日 08:48:49 +00:00Commented Dec 21, 2016 at 8:48
Explore related questions
See similar questions with these tags.