You need to enable logging for the the following categories:
org.hibernate.SQL
- set to debug
to log all SQL DML statements as they are executed
org.hibernate.type
- set to trace
to log all JDBC parameters
So a log4j configuration could look like:
# logs the SQL statements
log4j.logger.org.hibernate.SQL=debug
# Logs the JDBC parameters passed to a query
log4j.logger.org.hibernate.type=trace
The first is equivalent to hibernate.show_sql=true
legacy property, the second prints the bound parameters among other things.
Another solution (non hibernate based) would be to use a JDBC proxy driver like P6Spy.
I would personally recommend keeping your mapping on the server side. You've probably done a lot of work building up your design to the point it's at now; don't throw that away.
Consider what a web service is. It is not merely an abstraction over your ORM; it is a contract. It is a public API for your clients, both internal and external.
A public API should have little if any reason to change. Almost any change to an API, aside from adding new types and methods, is a breaking change. But your domain model is not going to be so strict. You will need to change it from time to time as you add new features or discover flaws in the original design. You want to be able to ensure that changes to your internal model do not cause cascading changes through the service's contract.
It's actually a common practice (I won't insult readers with the phrase "best practice") to create specific Request
and Response
classes for each message for a similar reason; it becomes much simpler to extend the capability of existing services and methods without them being breaking changes.
Clients probably don't want the exact same model that you use internally in the service. If you are your only client, then maybe this seems transparent, but if you have external clients and have seen just how far off their interpretation of your system can often be, then you'll understand the value of not allowing your perfect model to leak out the confines of the service API.
And sometimes, it's not even possible to send your model back through the API. There are many reasons why this can occur:
Cycles in the object graph. Perfectly fine in OOP; disastrous in serialization. You end up having to make painful permanent choices about which "direction" the graph must be serialized in. On the other hand, if you use a DTO, you can serialize in whichever direction you want, whatever suits the task at hand.
Attempting to use certain types of inheritance mechanisms over SOAP/REST can be a kludge at best. The old-style XML serializer at least supports xs:choice
; DataContract
doesn't, and I won't quibble over rationale, but suffice it to say that you probably have some polymorphism in your rich domain model and it's damn near impossible to channel that through the web service.
Lazy/deferred loading, which you probably make use of if you use an ORM. It's awkward enough making sure it gets serialized properly - for example, using Linq to SQL entities, WCF doesn't even trigger the lazy loader, it'll just put null
into that field unless you load it manually - but the problem gets even worse for data coming back in. Something as simple as a List<T>
auto-property that's initialized in the constructor - common enough in a domain model - simply does not work in WCF, because it doesn't invoke your constructor. Instead you have to add an [OnDeserializing]
initializer method, and you really don't want to clutter up your domain model with this garbage.
I also just noticed the parenthetical remark that you use NHibernate. Consider that interfaces like IList<T>
cannot be serialized at all over a web service! If you use POCO classes with NHibernate, as most of us do, then this simply won't work, period.
There will also likely be many instances when your internal domain model simply does not match the needs of the client, and it makes no sense to change your domain model to accommodate those needs. As an example of this, let's take something as simple as an invoice. It needs to show:
- Information about the account (account number, name, etc.)
- Invoice-specific data (invoice number, date, due date, etc.)
- A/R-level information (previous balance, late charges, new balance)
- Product or service information for everything on the invoice;
- Etc.
This probably fits fine within a domain model. But what if the client wants to run a report that shows 1200 of these invoices? Some sort of reconciliation report?
This sucks for serialization. Now you're sending 1200 invoices with the same data being serialized over and over again - same accounts, same products, same A/R. Internally, your application is keeping track of all the links; it knows the Invoice #35 and Invoice #45 are for the same customer and thus share a Customer
reference; all of this information is lost upon serialization and you end up sending a ridiculous amount of redundant data.
What you really want is to send a custom report that includes:
- All accounts included in the report, and their A/R;
- All products included in the report;
- All of the invoices, with Product and Account IDs only.
You need to perform additional "normalization" on your outgoing data before you send it to the client if you want to avoid the massive redundancy. This heavily favours the DTO approach; it does not make sense to have this structure in your domain model because your domain model already takes care of redundancies, in its own way.
I hope those are enough examples and enough rationale to convince you to keep your mappings from Domain <--> Service Contract intact. You've done absolutely the right thing so far, you have a great design, and it would be a shame to negate all that effort in favour of something that could lead to major headaches later on.
Best Answer
I'm assuming by CQS you mean the DDD architectural pattern aka CQRS, not strictly the traditional CQS principle.
I'd still use NHibernate for your read only model. There are many advantages such as future and multi queries, lazy/eager loading etc... that will optimize DB chattiness. Additionally, it will be easier to compose queries with an ORM if the UI allows for the user to essentially change the where clause.
Regarding how to technically handle a read only model, you can mark an entity immutable with NHibernate. You could simply mark all of your read model entities immutable. Also, I don't think you can update projections in NHibernate so that is another option to go for as your read only model (Someone please correct me if I am wrong as I am not 100% sure).
Regarding ugly or impossible NH mappings: NH can map to views and stored procedures, so I think it would be fine to use these when you need to. Views are probably a bit more flexible than stored procedures for a read only scenario because your SQL will still be dynamic. However, If you need read / write for any of these flattened structures I'd map to a store procedure.