So I've a class that is some kind of wrapper of a state machine for a multipart upload and it's database writes/reads. Everytime something is uploaded via REST basically the following happens
MyMultipartUpload mpu = multipartUploadFactory.create(...);
mpu.load(request.mpuID); //sets mutex lock
if (mpu.state != MpuState.UPLOAD) {
throw...
}
mpu.addPart(hugeBinaryBlob); //mpu hold a persistency interface which allows saving to the database
...
Now I've a mpu.process()
method that can be called when all parts arrived
MyMultipartUpload mpu = multipartUploadFactory.create(...);
mpu.load(request.mpuID); //sets mutex lock
if (mpu.state != MpuState.UPLOAD_COMPLETED) {
throw...
}
mpu.process();
...
The mpu.process()
method loads all parts one by one and first calls the private method mpu.validate()
, does some data transformation, saves the new representation form and then triggers an async job.
// process()
List<Part> parts = persistency.loadPartsForId(mpuID);
for (Part part : parts) {
var result = validate(part);
if (result.containsErrors()) {
setState(MpuState.ERROR);
return;
}
var newForm = transformData(part);
persistency.savePart(newForm);
}
Now my MyMultipartUpload
coding has become super long (both validate
and transformData
have many LoC) and I'm wondering how I could refactor it into more classes (which would also help with unit tests).
What I thought about so far:
Have a
ValidationService
andTransformationService
interface, where I can pass the part's content and do the validation / transformation. The service implementation is passed to the constructor ofMyMultipartUpload
. Saving in the database etc must still be done by the caller.Have a
ProcessingService
interface, where I pass a wholeMyMultipartUpload
object. Here the service would have to do the saving of data and validation/transformation itself. This means I need to create a service with access to the persistency (same database tables MyMultipartUpload writes to via the persistency interface). This also means it must be ensured that the lock on the object is present while processing.
Is either of them a good approach?
PathValidator
interface which does simply that, validate and return errors. It's decoupled from the other processing, because it's only job is to call a validator that was injected. So yeah your "take the big picture" approach was helpful, if you understand what I try to express :)