Doorgaan naar hoofdcontent

Data sharding and handling it in code

So I was at a project where data was somewhat sharded. We had a customer, which was part of a customer database (accessible by webservice calls), however, we had some additional customer data. This led to the (I think somewhat unfortunate) situation where the application reasoned from their own business object, and, when necessary, retrieved data from the relation system.

I feel this is flawed, ideally the entirety of the customer data should be invisible to the business logic of the application. Where I normally would like to have an anemic domain model, this data sharding makes it hard. Let's say you have relation R, and our extended data S. In our application, we want to reason about RS (or at least, not have to make that distinction). So maybe S has some additional attribute which may (or may not) be filled, depending on whether R is available or not. That sounds like a nice idea, but everywhere when R is necessary (and S is available), R needs to be retrieved.
And that is where the flaw is, since this doesn't help reusability. Our components are built using S, and it is quite possible that we retrieve R multiple times!

That is not desired. We can go two ways.
* we have a factory which always enriches our content upon the start, so we never have S, we always ensure we have RS
* We have a thicker model, in which S can retrieve R when necessary. This entails giving S the means to retrieve R.

To me, the last option feels the cleanest, however, both leave the issue of synchronisation. What happens when R is (implicitly) updated? This sounds like our entitymanager, however an entitymanager does so much more. Yet, we might do something with it.

I really should test this, but so far, it's just a Poc:
Usually, we have some sort of Factory for S objects, which contains the entitymanager and persists S.
However, what we can do is have that Factory also insert the soap client, so S can retrieve R if necessary. Moreover, we *can* use the entitymanager! (whoopee!).
If we register an entitylistener, and use some transient fields, we can have the cake and eat it:
@EntityListeners({CustomerDataPersister.class})
public class ApplicationCustomerData {

    private String customerKey;

    @Transient
    private Customer customer;

    @Transient
    private CustomerWebserviceClient webserviceClient;

    public Customer getCustomer() {
        if(customer == null) {
            customer = webserviceClient.retrieve(key);
        }
        return customer;
    }

    public boolean isEnhanced() {
        return customer!= null;
    }
}


public class CustomerDataPersister {
    // use CDI to inject the webserviceclient, see below
    @PrePersist // and others
    public void update(ApplicationCustomerData d)
        if ( d.isEnhanced() ) {
            webserviceClient.update(d.getCustomer());
        }
    }
}


Now we're talking! We can even do this all behind the scenes using more entitylisteners, say, one which inserts the webserviceclient through @postload annotations.

However, the big secret is injecting the webserviceclient in the first place. We need this in the listeners, but they need not be managed beans! Luckily for us, JPA 2.1 supports this (JPA 2.0 does not). Stackoverflow has this covered!
http://stackoverflow.com/questions/10765508/cdi-injection-in-entitylisteners

Now we all of a sudden have an S object which implicitly loads R when necessary. Our entire application is none the wiser, and all of a sudden a complex issue which would eat through our entire application is neatly tucked away!




Reacties

Populaire posts van deze blog

Spring's conditional annotation with properties

Spring has a nice @Conditional annotation, to have the option to have beans be available in the context depending a specific condition (Of course, this can also be realized by using @Configuration objects, but that's a different post). Ideally, we'd have the option to have a condition evaluate to true or false depending on a property. Sadly, Spring does not support that out of the box. Googling and looking around gives a partial solution, but the complete one here, so we won't forget: /** * Components annotated with ConditionalOnProperty will be registered in the spring context depending on the value of a * property defined in the propertiesBeanName properties Bean. */ @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Conditional(OnPropertyCondition.class) public @interface ConditionalOnProperty { /** * The name of the property. If not found, it will evaluate to false. */ String value(); /** * if the p...

On SSL certificate generation

 So, more stuff I always forget... how to properly generate SSL certs. Well, easiest is with openssl (of course) Something like: openssl req -new -newkey rsa:2048 -nodes  -sha256 -subj "/C=NL/ST=Utrecht/L=Utrecht/O=Cooperatieve Rabobank U.A./OU=RASS Groep ICT/CN=my-common-name.host.nl" -keyout somename-prod.key -out somename-prod.csr   That can get you a certificate sign request (csr) and the appropriate key.   Of course, you want to then import those keys into a keystore. The trick to doing that is to convert it to a pkcs12 format where it can have the certificate and the key combined.   openssl pkcs12 -export -inkey somename-prod.key -in somename-prod.rabobank.nl.crt -out somename-prod.p12  Note that the crt is the signed certificate, acquired through getting the csr generated above approved..   This p12 file you can import using something like KeyStore Explorer.   After that, you can also append the root certificates of the original cert, to en...

Using spring's @transactional to only roll back when you really want to

In spring you can use the @Transactional annotation to marcate public methods as transactions. Any exception thrown in such an exception causes a rollback... Any exception? No, spring only does so on runtime exceptions. Checked exceptions are allowed and do not result in a rollback. And even then, you can allow some transactions to rollback, or not, with the proper properties for the transactions. For examle, @Transactional(rollbackFor="MyCheckedException.class") will rollback for a specific checked exception, and similarly, you can use @Transactional(noRollbackFor="MyUncheckedException.class") for unchecked exceptions. Of course, you might want to make it a little simpler on yourself. This article on stackoverflow shows us a different way: we can use our own transaction handler by overriding spring's. Let's have a sample interface which defines whether an exception should perform a rollback: interface ConfigurableRollback shouldRollbackOnE...