The code for this and all other entries can be found here: http://github.com/robashton/RavenGallery/
Let’s think of a few pieces of (basic) functionality our image gallery will need at least in order to be classified as a functional image gallery
Starting with this simplicity, it’s fairly obvious that Image is going to be a first class citizen in our application we can safely create the document and entity in the standard manner
1: public class ImageDocument
2: {
3: public string Id
4: {
5: get;
6: set;
7: }
8:
9: public string OwnerUserId
10: {
11: get;
12: set;
13: }
14:
15: public DateTime DateUploaded
16: {
17: get;
18: set;
19: }
20:
21: public string Title
22: {
23: get;
24: set;
25: }
26:
27: public string Filename
28: {
29: get;
30: set;
31: }
32:
33: public List<ImageTagDocument> Tags
34: {
35: get;
36: set;
37: }
38:
39: public ImageDocument() { Tags = new List<ImageTagDocument>(); }
40: }
1: public class ImageTagDocument
2: {
3: public string Name
4: {
5: get;
6: set;
7: }
8: }
Note: I’ve included a list of tags on my document, and I haven’t had to do anything special, they will get saved along with the entire ImageDocument automatically
The interaction
Ignoring the process of how we upload the image within our actual MVC2 project (this series is about RavenDB!), assume a command has been sent containing the user id, title, tags and actual bytes for the file upload.
I’ve defined a service called ImageUploaderService who takes in the relevant information to be able to do something about this file upload problem.
1: public interface IImageUploaderService
2: {
3: void UploadUserImage(User user, string title, string[] tags, Byte[] data);
4: }
When I come to implement that in the default implementation (called ImageUploaderService), it is obvious that two things need to happen; it will need to process the image data appropriately and put it somewhere for storage, and it will need to create the actual entity containing the information about this image.
I’m going to delegate the problem of “where to put files” to yet another service that I place in a new area of my project called “infrastructure”, and I christen it “IFileStorageService”
1: public interface IFileStorageService
2: {
3: void StoreFile(string filename, Byte[] bytes);
4: }
With that done, we can safely ignore this problem and focus on creating the new Image entity and persisting that, so let’s start with the constructor:
1: public Image(User owner, string title, string filename)
2: {
3: innerDocument = new ImageDocument()
4: {
5: DateUploaded = DateTime.Now,
6: OwnerUserId = owner.UserId,
7: Title = title,
8: Filename = filename
9: };
10: }
This is borderline ‘move it to a factory’ material, but nothing is majorly different here to how we constructed the User entity, other than we take in a User entity and copy its id across to our document so we know who created it.
As shown here, I’m not against adding properties to my entities to expose data so long as they’re read only and actually needed – I don’t consider this to be a waste of time as we’ll see when we reach the ‘Views’ in our application we hardly ever need data from the entities themselves. What I don’t do is go through and create properties to mirror what I’m storing in the document itself, that would be time consuming and pointless.
My ImageUploaderService in the meantime has been left with no implementation, so after writing the tests (which I have done), with our existing infrastructure it looks something like this
1: public class ImageUploaderService : IImageUploaderService
2: {
3: private IFileStorageService fileStorageService;
4: private IImageRepository imageRepository;
5:
6: public ImageUploaderService(IFileStorageService fileStorageService, IImageRepository imageRepository)
7: {
8: this.fileStorageService = fileStorageService;
9: this.imageRepository = imageRepository;
10: }
11:
12: public void UploadUserImage(User user, string title, string[] tags, byte[] data)
13: {
14: string filename = String.Format("Images/{0}", Guid.NewGuid().ToString());
15: fileStorageService.StoreFile(filename, data);
16:
17: Image newImage = new Image(user, filename, title);
18: foreach (var tag in tags)
19: {
20: newImage.AddTag(tag);
21: }
22: imageRepository.Add(newImage);
23: }
24: }
This is a good thing, we’ve got all the required behaviour nicely wrapped up and we’re still not worrying about persistence (I like not worrying about persistence) and although it’s simplistic, the entities are responsible for enacting change and therefore guarding the state of the underlying documents.
In the next entry we’ll look at how we actually store that file using the RavenDB attachments API.
2020 © Rob Ashton. ALL Rights Reserved.