Linking together Repository pattern, caching and web-service - c#

I'm try to understand Repository pattern to implement it in my app. And I'm stuck with it in a some way.
Here is a simplified algorithm of how the app is accessing to a data:
At first time the app has no data. It needs to connect to a web-service to get this data. So all the low-level logic of interaction with the web-service will be hiding behind the WebServiceRepository class. All the data passed from the web-service to the app will be cached.
Next time when the app will request the data this data will be searched in the cache before requesting them from the web-service. Cache represents itself as a database and XML files and will be accessed through the CacheRepository.
The cached data can be in three states: valid (can be shown to user), invalid (old data that can't be shown) and partly-valid (can be shown but must be updated as soon as possible).
a) If the cached data is valid then after we get them we can stop.
b) If the chached data is invalid or partly-valid we need to access WebServiceRepository. If the access to the web-service is ended with a success then requested data will be cached and then will be showed to user (I think this must be implemented as a second call to the CacheRepository).
c) So the entry point of the data access is the CacheRepository. Web-service will be called only if there is no fully valid cache.
I can't figure out where to place the logic of verifying the cache (valid/invalid/partly-valid)? Where to place the call of the WebServiceRepository? I think that this logic can't be placed in no one of Repositories, because of violation the Single Responsibility Principle (SRP) from SOLID.
Should I implement some sort of RepositoryService and put all the logic in it? Or maybe is there a way to link WebServiceRepository and WebServiceRepository?
What are patterns and approaches to implement that?
Another question is how to get partly-valid data from cache and then request the web-service in the one method's call? I think to use delegates and events. Is there other approaches?
Please, give an advice. Which is the correct way to link all the functionality listed above?
P.S. Maybe I described all a bit confusing. I can give some additional clarifications if needed.
P.P.S. Under CacheRepository (and under WebServiceRepository) I meant a set of repositories - CustomerCacheRepository, ProductCacheRepository and so on. Thanks #hacktick for the comment.

if your webservice gives you crud methods for different entities create a repository for every entityroot.
if there are customers create a CustomerRepository. if there are documents with attachments as childs create a DocumentRepository that returns documents with attachments as a property.
a repository is only responsible for a specific type of entity (ie. customers or documents). repositories are not used for "cross cutting concerns" such as caching. (ie. your example of an CacheRepository)
inject (ie. StuctureMap) a IDataCache instance for every repository.
a call to Repository.GetAll() returns all entities for the current repository. every entity is registered in the cache. note the id of that object in the cache.
a call to Repository.FindById() checks the cache first for the id. if the object is valid return it.
notifications about invalidation of an object is routed to the cache. you could implement client-side invalidation or push messages from the server to the client for example via messagequeues.
information about the status whether an object is currently valid or not should not be stored in the entity object itself but rather only in the cache.

Related

Reuse a data object model across multiple requests

Newbie to the whole ASP.Net conundrum !!
So I have a set of Web API's (Actions) in a controller which will be called in succession to each other from the client. All the API's depend on a data model object fetched from the database. Right now I have a DAO layer which fetches this data and transparently caches it. So there is no immediate issue as there is no round trip to database for each API call. But the DAO layer is maintained by a different team and makes no guarantee that the cache will continue to exist or it's behaviour won't change.
Now the properties or attributes of the model object would change but not too often. So If I refer the API calls made in succession from a client as a bundle, then I can safely assume that the bundle can actually query this data once and use it without having to worry about change in value. How can I achieve this ? Is there a design pattern somewhere in ASP.Net world which I can use ? What I would like is to fetch this value at a periodic interval and refresh it in case one of the API calls failed indicating the underlying values have changed.
There are a few techniques that might be used. First of all, is there a reason for the need of a second cache, because your Data Access Layer already has it, right?
You can place a cache on the Web API response level by using a third party library called Strathweb.CacheOutput, and:
CacheOutput will take care of server side caching and set the appropriate client side (response) headers for you.
You can also cache the data from your data access layer by using a more manual approach by using the MemoryCache from System.Runtime.Caching.
Depending on what's the infrastructure available, distributed caches like Cassandra or Redis may be the best choice.

DDD Child object references

I have an invoice object, which consists of items, and each item has a relation to service.
Following structure.
{
"invoiceId" : "dsr23343",
"items":{
"id":1,
"service":{
"serviceCode":"HTT"
}
}
}
One of my requirements is that the item should not have a relation to service which does not exist in our system.
From my understanding, domain objects should never enter in an invalid state.
So what I am doing is following:
var service = new Service("SomeService");
var item = new Item(service);
invoice.AddItem(item);
My question is, should i require AddItem function to receive Repository as second parameter, and throw exception if Service does not exist in database?
My question is, should i require AddItem function to receive Repository as second parameter, and throw exception if Service does not exist in database?
Short answer: sure, why not?
Longer answer...
If Service and Invoice are part of the same aggregate, then the repository is unnecessary -- just look at the state of the aggregate. So what follows assumes that there is a transaction boundary between the Invoice and the Service.
Using a Repository as the argument is a bit too much stuff -- Invoice doesn't need to load the Service, it just needs to know if the Service exists. So instead of putting a Repository in the method signature, you could use a DomainService that supports the "does this service exist?" query.
(The implementation of the DomainService probably does a lookup in the Repository -- we're not doing magic here, we're just isolating Invoice from implementation details it doesn't need to know about).
Using the more restrictive interface in the signature documents clearly what the integration contract is between these components.
That said, the requirement is very suspicious. If Service and Invoice are in different aggregates, then they potentially have different life cycles. What is supposed to happen when you try to load an invoice, that includes an item that references a service which no longer exists? Is that use case supposed to explode? if so, it's going to be hard to edit the invoice to fix the problem....
What if, while you are adding the item to the invoice, some other thread is deleting the service...?
Review Udi Dahan's essay: Race Conditions Don't Exist. Executive summary - if your model is sensitive to microsecond variations in timing, you probably aren't modelling your business.
You've got at least three other alternatives to protect this "invariant".
One is at the client level; if you don't let the client produce invalid service codes, then you aren't going to have this problem. Input validation belongs in the client component or in the application component, not so much the model. That is, it's the sort of thing that you might check when the application is constructing the ServiceCode from the DTO that traveled across the process boundary.
One is downstream of the model - if you can detect invoice items that reference service codes that are invalid, then you can broadcast an exception report, and use the contingency response process to manage the problem. Consistency issues that are rare, cheap to detect, easy to fix don't need tight validation in the domain model.
One is within the model itself - if creation of an invoice item is tightly coupled to the lifetime of a service, then maybe the item is created by the service, rather than by the invoice. For example
class Service {
reportUsage(Customer, TimePeriod)
}
Wouldn't be an unusual looking signature, and you can probably be confident that the Service raising a domain event is going to correctly report its own ServiceCode.

Maintaining CQS When Tracking Number Of Queries

In my web app I'm tracking view counts on pages.
Right now, the action in the controller issues a command to the data layer to increment the view count on the model before returning the result of the query.
This action seems to break the rules of Command-Query-Separation, since with the request the user agent is submitting a query and unwittingly issuing a command (to increment the view count)
What architectural decisions need to be taken to maintain the Command-Query-Separation in this action?
You should consider CQS relative to the conceptual level of the operation in question. Here are some examples that all seem to violate CQS, but only do so on a different conceptual level:
A ReadFile call on a file system object does not modify the file - but it canupdate the last accessed timestamp on the file.
A FindById call to a repository should not change the database - but it can very well add the queried object to a cache.
A GET operation on a REST API should not change the model - but it can update statistical data.
These examples have one thing in common: They maintain the state of the model the client works on, but they do modify data outside of that model. And this is not a violation of CQS.
Another way to see this is through the principle of least surprise. A client of the above mentioned REST API would not expect the model to change with a GET request, but he doesn't care if the API updates a statistical counter.

DDD - transient validation using an aggregate

I have a particular scenario where an aggregate has behavior to check whether an address is valid. This validation is triggered on the aggregate via inline ajax form validation on a web site. In between the aggregate and the web site is an application service which orchestrates the two.
As it stands, I create what is essentially an empty aggregate and set the address property so the check can be done. Based on this I return true or false back to the web site (ASP.NET MVC). This doesn't seem like the right approach on the context of DDD.
public bool IsAddressAvailable(string address)
{
var aggregate = new Aggregate
{
Address = address
};
return aggregate.IsAddressValid();
}
What options do I have that would work better using DDD? I was consider separating it out into a domain service. Any advice would be appreciated!
Normally your aggregates should not expose Get- methods, you always want to follow Tell-Don't-Ask principle.
If something needs to be done - then you call an aggregate method and it makes it done.
But you normally don't want to ask Aggregate if the data is valid or not. Especially if you already have a service that does this job for you, why mixing this "validation" with aggregates?
The rule of thumb is:
If something is not needed for Aggregate's behavior it doesn't need to be a part of the aggregate
You only pass valid data into your domain. It means that when you call an aggregate behavior asking it to do something for you, the data you pass is already validated. You don't want to pollute your domain with data validation / if-else branches, etc. Keep it straight and simple.
In your case, as far as I understand, you only need to validate user's input, so you don't need to bother your domain to do it for two reasons:
You don't do anything, don't change system's state. It is considered to be a "read" operation, do it straightforward (call your service, validate against some tables, etc)
You cannot rely on validation result. Now it tells you "correct" and in 10 milliseconds (while you get the response over the wire, while HTML is rendered in browser, etc) it is already a history, it MAY change any time. So this validation is just a guidance, no more.
Therefore if you only need "read-only" validation just do it against your service.
If you need to validate user's data as a part of operation then do it before you call the domain (perhaps in your command handler).
And be aware of racing conditions (DB unique constraints can help).
You should also consider reading this to think deeper about set validation: http://codebetter.com/gregyoung/2010/08/12/eventual-consistency-and-set-validation/

Pattern or design for sync database with external provider? (best practice)

Your advice is needed! I'm just out for some sort of pseudo-code/idea of what way to go that are robust and reliable. Maybe there exist a usefull pattern for the purpose?
void AddDevice(string itemId);
I have a interface with some methods (above is one). In a new class, that implements the interface, there are an external provider involved which need to be informed of updates in the class.
The class itself is get/sets information to a sql server database. Some (not all) of the information must be pushed to the external provider.
This give me two scenarios (which I ask for help)
WriteOnlytoDatabase = true / false
I would like to use same method in both cases, without using a method bool parameter. Is that possible? Could a delegate being used to switch between the difference? Please remember it's a interface here (that GUI talks to).
Two transfers, how to track errors
Because we do two transfers (database, external provider) there can be error that make one or other unavailable. If error on the external provider, I think of some sort of "undone actions queue" to handle..
Advices are welcome..
This could be solved on a dousin of ways but there are more or less good designs :)
[Reply from Matías below was wroted
before my edit of question]
First of all, I would point to the fact that maybe there's some available synchronization mechanism in the market or open source community that can do it for you, outside your code. If this is the case, I'll suggest that I wouldn't implement my own way of synchronize such data. I'd prefer to let such tool do it for me.
Perhaps this isn't your case and your data couldn't be sync'd with an standard or known tool, we need to think about another solution.
I believe some entity isn't responsible of sync'ing with itself in another storage. That should be a task for the layer between business and that storage: the data layer.
Business access to the data without any detail of where to retrieve it. It just get or change business objects' states, or removes them from the store.
It's the business who in some case would require some argument like "ActionKind" - an enumeration - and, since business would rely on some data access layer, some code there would do something depending on the "ActionKind".
This "ActionKind" would let data access layer to choose an implementation of "how and where to store data".
That data access layer would have some "event" or "trigger" that would fire when some change has been made to one of underlying storage devices, and some handler(s) would manage to synchronize data in all other stores.
The "event" or "trigger" handler would be implemented directly in code (hard-coding) or with some interface like "IDataSynchronizer" (choose your own identifier, it's just an example) having a "Synchronize" method, that would be called when some data changed in any storage.
I believe using some approach like this one you'd have less problems with synchronization and you won't need to care about if "1st device has the data, 2nd no, so, I need to check blah blah..."! :)

Categories

Resources