You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@cxf.apache.org by geirgp <ge...@gmail.com> on 2009/03/02 12:20:07 UTC

OO <-> WS paradigm mismatch

Now, this is more a Web Service design issue than a CXF issue, but I am
wondering how CXF/Java users solve/work around it. 

The WS definition is limited compared to how you can model data in an OO
language. A classic example is bidirectional relationships, widely used in
JPA/Hibernate applications, which would lead to cyclic references and a hard
crash if used with CXF. I've written my thoughts below, but have no clear
idea about what is the best approach on how to work around this issue. What
are your experiences/best practices?

I will use the Author-Book model as an example to express my thoughts:

public class Author{
   private Long id;
   private String name;
   private Collection<Book> books;
}

public class Book{
   private Long id;
   private String title;
   private Author author;
}

In its current form this model will cause a cyclic reference error when used
with CXF because Author holds a collection of Books which refers back to the
Author. I have come up with 3 strategies to work around this issue and
"CXF-enable" my service:

A) 
Modify domain model to have no cyclic dependencies by removing either
Book.author OR Author.books. Example:

public class Author{
   private Long id;
   private String name;
   private Collection<Book> books;
}

public class Book{
   private Long id;
   private String title;

}
Pros:
* Model can still be traversed (one direction) without making another call
to the service.
cons:
* Model can only be traversed one way (Author->Books). When client has a
Book, he must make another call to the service to get the Author. More
complexity and more costly in terms of performance
* I have to change my server-side business code which depends on traversing
objects, they can no longer access book.getAuthor(). Instead I will have to
introduce an extra dao-call, AuthorDao.findByBookId(..). This is not good in
terms of both complexity and performance.

B)
Another possible solution is to introduce a layer between my DAO and
WebService implementation which nulls out potentional cyclic references,
example:

@WebService
public class LibraryService {
 private AuthorDao authorDao;
 private BookDao bookDao;
 
 public Author findAuthorById(Long id) {
  Author author = authorDao.find( id );
  for( Book book : author.getBooks() ) {
   book.setAuthor( null );
  }
  return author;
 }
 
 public Book findBookById( Long id ) {
  Book book = bookDao.find( id );
  book.getAuthor().setBooks( null );
  return book ;
 }
}

Pros:
* I can keep my Domain model and traverse both ways (serverside)
* Model can be traversed all ways, but only one way at a time (if that makes
sense). See cons for details about this 


Cons:
* Inconsitency in model which cannot be traversed both ways on client side.
Books belong to an Author yet Book.author is null. 
** When retrieveing a book, you can traverse the book and find its author,
but not go back and find the author's books. 
** When retrieving an author, you can traverse the author and find her
books, but not go back and find the books' author. 
* SOAP is only ONE of the remoting protocols used for exposing my web
services - Spring HTTP Invoker, which is capable of handling cyclic
references, is another one. I will have to maintain two service
implementations, one for CXF/SOAP where Book.author is nulled ouit, and one
for SPRING where Book.author is kept.

C) 
Create a WS-specific domain model and introduce mapping between this model
and my "proper" domain model:

public class AuthorWS{
   private Long id;
   private String name;
   private Collection<Long> bookIds;
}

public class BookWS{
   private Long id;
   private String title;
   private Long authorId;
}


Pros:
* I can keep my domain model and traverse the model at server-side. 
* Data returned from my Web Service will be consistent: no references are
nulled out. 

Cons:
* Data returned from Web Service is "flat" instead of "good" OO-model: When
I have found a Book, i have to make another call to get the Author (and vice
versa). This will affect both client-code design and performance.
* Complexity at serverside: introduces extra layer which handles mapping.

D) 
What have I missed? What is your best practice?
-- 
View this message in context: http://www.nabble.com/OO-%3C-%3E-WS-paradigm-mismatch-tp22286364p22286364.html
Sent from the cxf-user mailing list archive at Nabble.com.


Re: OO <-> WS paradigm mismatch

Posted by Daniel Kulp <dk...@apache.org>.
D) Use some of the JAXB ways of dealing with cyclic graphs.  See:
https://jaxb.dev.java.net/guide/Mapping_cyclic_references_to_XML.html

Many of them (parent pointer and CycleRecoverable) wouldn't show the cycle in 
the Schema though so toolkits would see something like (A) in the schema, but 
your Hibernate code could work as is.   

Dan




On Mon March 2 2009 6:20:07 am geirgp wrote:
> Now, this is more a Web Service design issue than a CXF issue, but I am
> wondering how CXF/Java users solve/work around it.
>
> The WS definition is limited compared to how you can model data in an OO
> language. A classic example is bidirectional relationships, widely used in
> JPA/Hibernate applications, which would lead to cyclic references and a
> hard crash if used with CXF. I've written my thoughts below, but have no
> clear idea about what is the best approach on how to work around this
> issue. What are your experiences/best practices?
>
> I will use the Author-Book model as an example to express my thoughts:
>
> public class Author{
>    private Long id;
>    private String name;
>    private Collection<Book> books;
> }
>
> public class Book{
>    private Long id;
>    private String title;
>    private Author author;
> }
>
> In its current form this model will cause a cyclic reference error when
> used with CXF because Author holds a collection of Books which refers back
> to the Author. I have come up with 3 strategies to work around this issue
> and "CXF-enable" my service:
>
> A)
> Modify domain model to have no cyclic dependencies by removing either
> Book.author OR Author.books. Example:
>
> public class Author{
>    private Long id;
>    private String name;
>    private Collection<Book> books;
> }
>
> public class Book{
>    private Long id;
>    private String title;
>
> }
> Pros:
> * Model can still be traversed (one direction) without making another call
> to the service.
> cons:
> * Model can only be traversed one way (Author->Books). When client has a
> Book, he must make another call to the service to get the Author. More
> complexity and more costly in terms of performance
> * I have to change my server-side business code which depends on traversing
> objects, they can no longer access book.getAuthor(). Instead I will have to
> introduce an extra dao-call, AuthorDao.findByBookId(..). This is not good
> in terms of both complexity and performance.
>
> B)
> Another possible solution is to introduce a layer between my DAO and
> WebService implementation which nulls out potentional cyclic references,
> example:
>
> @WebService
> public class LibraryService {
>  private AuthorDao authorDao;
>  private BookDao bookDao;
>
>  public Author findAuthorById(Long id) {
>   Author author = authorDao.find( id );
>   for( Book book : author.getBooks() ) {
>    book.setAuthor( null );
>   }
>   return author;
>  }
>
>  public Book findBookById( Long id ) {
>   Book book = bookDao.find( id );
>   book.getAuthor().setBooks( null );
>   return book ;
>  }
> }
>
> Pros:
> * I can keep my Domain model and traverse both ways (serverside)
> * Model can be traversed all ways, but only one way at a time (if that
> makes sense). See cons for details about this
>
>
> Cons:
> * Inconsitency in model which cannot be traversed both ways on client side.
> Books belong to an Author yet Book.author is null.
> ** When retrieveing a book, you can traverse the book and find its author,
> but not go back and find the author's books.
> ** When retrieving an author, you can traverse the author and find her
> books, but not go back and find the books' author.
> * SOAP is only ONE of the remoting protocols used for exposing my web
> services - Spring HTTP Invoker, which is capable of handling cyclic
> references, is another one. I will have to maintain two service
> implementations, one for CXF/SOAP where Book.author is nulled ouit, and one
> for SPRING where Book.author is kept.
>
> C)
> Create a WS-specific domain model and introduce mapping between this model
> and my "proper" domain model:
>
> public class AuthorWS{
>    private Long id;
>    private String name;
>    private Collection<Long> bookIds;
> }
>
> public class BookWS{
>    private Long id;
>    private String title;
>    private Long authorId;
> }
>
>
> Pros:
> * I can keep my domain model and traverse the model at server-side.
> * Data returned from my Web Service will be consistent: no references are
> nulled out.
>
> Cons:
> * Data returned from Web Service is "flat" instead of "good" OO-model: When
> I have found a Book, i have to make another call to get the Author (and
> vice versa). This will affect both client-code design and performance.
> * Complexity at serverside: introduces extra layer which handles mapping.
>
> D)
> What have I missed? What is your best practice?

-- 
Daniel Kulp
dkulp@apache.org
http://www.dankulp.com/blog

Re: OO <-> WS paradigm mismatch

Posted by Andrew Clegg <an...@nervechannel.com>.
One other option (my preferred way), similar to your option C.

Think in terms of services and messages, rather than objects, classes
and methods. Get away from the "services as a way of doing remote Java
method invocation" mindset. Design your services WSDL-first (if
possible) for all the good reasons discussed here:

http://static.springframework.org/spring-ws/sites/1.5/reference/html/why-contract-first.html

As for your specific example, it depends on the requirements of your
application, but you could provide four operations:

GetAuthorByID

GetBookByID

GetAuthorWithBooksByID

GetBookWithAuthorsByID

The first two just return an author or book element respectively, but
the last two have the books for that author, or authors for that book,
nested within.

If it would make things smoother, you can also provide:

GetAuthorsByBookID

GetBooksByAuthorID

These can remove the redundancy when the client already has the book,
and wants the authors, or vice versa. But only write these if you
actually need them.

Design operations that reflect your actual business logic, don't worry
about things like cyclic dependencies, and maybe they won't be an
issue in practice. All it might mean is a little more 'glue' code to
mediate between your web services and your persistent JPA objects.

This also means you'll end up with portable and interoperable WSDLs,
it decouples your service contract from any one programming language
or toolkit, and allows the WSDL to will remain stable even if the
server-side data model has to change.

Andrew.

2009/3/2 geirgp <ge...@gmail.com>:
>
> Now, this is more a Web Service design issue than a CXF issue, but I am
> wondering how CXF/Java users solve/work around it.
>
> The WS definition is limited compared to how you can model data in an OO
> language. A classic example is bidirectional relationships, widely used in
> JPA/Hibernate applications, which would lead to cyclic references and a hard
> crash if used with CXF. I've written my thoughts below, but have no clear
> idea about what is the best approach on how to work around this issue. What
> are your experiences/best practices?
>
> I will use the Author-Book model as an example to express my thoughts:
>
> public class Author{
>   private Long id;
>   private String name;
>   private Collection<Book> books;
> }
>
> public class Book{
>   private Long id;
>   private String title;
>   private Author author;
> }
>
> In its current form this model will cause a cyclic reference error when used
> with CXF because Author holds a collection of Books which refers back to the
> Author. I have come up with 3 strategies to work around this issue and
> "CXF-enable" my service:
>
> A)
> Modify domain model to have no cyclic dependencies by removing either
> Book.author OR Author.books. Example:
>
> public class Author{
>   private Long id;
>   private String name;
>   private Collection<Book> books;
> }
>
> public class Book{
>   private Long id;
>   private String title;
>
> }
> Pros:
> * Model can still be traversed (one direction) without making another call
> to the service.
> cons:
> * Model can only be traversed one way (Author->Books). When client has a
> Book, he must make another call to the service to get the Author. More
> complexity and more costly in terms of performance
> * I have to change my server-side business code which depends on traversing
> objects, they can no longer access book.getAuthor(). Instead I will have to
> introduce an extra dao-call, AuthorDao.findByBookId(..). This is not good in
> terms of both complexity and performance.
>
> B)
> Another possible solution is to introduce a layer between my DAO and
> WebService implementation which nulls out potentional cyclic references,
> example:
>
> @WebService
> public class LibraryService {
>  private AuthorDao authorDao;
>  private BookDao bookDao;
>
>  public Author findAuthorById(Long id) {
>  Author author = authorDao.find( id );
>  for( Book book : author.getBooks() ) {
>   book.setAuthor( null );
>  }
>  return author;
>  }
>
>  public Book findBookById( Long id ) {
>  Book book = bookDao.find( id );
>  book.getAuthor().setBooks( null );
>  return book ;
>  }
> }
>
> Pros:
> * I can keep my Domain model and traverse both ways (serverside)
> * Model can be traversed all ways, but only one way at a time (if that makes
> sense). See cons for details about this
>
>
> Cons:
> * Inconsitency in model which cannot be traversed both ways on client side.
> Books belong to an Author yet Book.author is null.
> ** When retrieveing a book, you can traverse the book and find its author,
> but not go back and find the author's books.
> ** When retrieving an author, you can traverse the author and find her
> books, but not go back and find the books' author.
> * SOAP is only ONE of the remoting protocols used for exposing my web
> services - Spring HTTP Invoker, which is capable of handling cyclic
> references, is another one. I will have to maintain two service
> implementations, one for CXF/SOAP where Book.author is nulled ouit, and one
> for SPRING where Book.author is kept.
>
> C)
> Create a WS-specific domain model and introduce mapping between this model
> and my "proper" domain model:
>
> public class AuthorWS{
>   private Long id;
>   private String name;
>   private Collection<Long> bookIds;
> }
>
> public class BookWS{
>   private Long id;
>   private String title;
>   private Long authorId;
> }
>
>
> Pros:
> * I can keep my domain model and traverse the model at server-side.
> * Data returned from my Web Service will be consistent: no references are
> nulled out.
>
> Cons:
> * Data returned from Web Service is "flat" instead of "good" OO-model: When
> I have found a Book, i have to make another call to get the Author (and vice
> versa). This will affect both client-code design and performance.
> * Complexity at serverside: introduces extra layer which handles mapping.
>
> D)
> What have I missed? What is your best practice?
> --
> View this message in context: http://www.nabble.com/OO-%3C-%3E-WS-paradigm-mismatch-tp22286364p22286364.html
> Sent from the cxf-user mailing list archive at Nabble.com.
>
>



-- 
:: http://biotext.org.uk/ ::

RE: OO <-> WS paradigm mismatch

Posted by Richard Grantham <rg...@limehousesoftware.co.uk>.
I do something similar to your option C but wrap the author and books in
another generic result object so you don't have to make more than one WS
call.

public class ResultWS {
   private Collection<AuthorWS> authors;
   private Collection<BookWS> books;
}

public class AuthorWS{
   private Long id;
   private String name;
   private Collection<Long> bookIds;
}

public class BookWS{
   private Long id;
   private String title;
   private Long authorId;
}

This allows you to add as much or as little information to what your
service returns depending on what the method does, and allow you to
traverse the information both ways. However, on the down side it
requires more client-side parsing and a detailed understanding on how to
interpret the results.

I would also point out that there is at least one WS client library (I
can't remember which off the top of my head) that doesn't like
recursive/cyclicle relationships in WSDLs. So depending on how your
service is to be used and by whom you may wish to avoid doing it.




Richard Grantham
Development

-------------------------------
rgrantham@limehousesoftware.co.uk
Limehouse Software Ltd

DDI: (020) 7566 3336
Main: (020) 7566 3320
Fax: (020) 7566 3321

Limehouse Software Ltd
Bridewell Gate
9 Bridewell Place
London
EC4V 6AW


Check out Limehouse Software's innovative solutions
www.limehousesoftware.co.uk - Transforming the way you publish and consult on information

The information contained in this e-mail or in any attachments is confidential and is intended solely for the named addressee only. Access to this e-mail by anyone else is unauthorised. If you are not the intended recipient, please notify Limehouse Software Ltd immediately by returning this e-mail to sender or calling 020 7566 3320 and do not read, use or disseminate the information. Opinions expressed in this e-mail are those of the sender and not necessarily the company. Although an active anti-virus policy is operated, the company accepts no liability for any damage caused by any virus transmitted by this e-mail, including any attachments.-----Original Message-----
From: geirgp [mailto:geirgp@gmail.com] 
Sent: 02 March 2009 11:20
To: users@cxf.apache.org
Subject: OO <-> WS paradigm mismatch


Now, this is more a Web Service design issue than a CXF issue, but I am
wondering how CXF/Java users solve/work around it. 

The WS definition is limited compared to how you can model data in an OO
language. A classic example is bidirectional relationships, widely used
in JPA/Hibernate applications, which would lead to cyclic references and
a hard crash if used with CXF. I've written my thoughts below, but have
no clear idea about what is the best approach on how to work around this
issue. What are your experiences/best practices?

I will use the Author-Book model as an example to express my thoughts:

public class Author{
   private Long id;
   private String name;
   private Collection<Book> books;
}

public class Book{
   private Long id;
   private String title;
   private Author author;
}

In its current form this model will cause a cyclic reference error when
used with CXF because Author holds a collection of Books which refers
back to the Author. I have come up with 3 strategies to work around this
issue and "CXF-enable" my service:

A)
Modify domain model to have no cyclic dependencies by removing either
Book.author OR Author.books. Example:

public class Author{
   private Long id;
   private String name;
   private Collection<Book> books;
}

public class Book{
   private Long id;
   private String title;

}
Pros:
* Model can still be traversed (one direction) without making another
call to the service.
cons:
* Model can only be traversed one way (Author->Books). When client has a
Book, he must make another call to the service to get the Author. More
complexity and more costly in terms of performance
* I have to change my server-side business code which depends on
traversing objects, they can no longer access book.getAuthor(). Instead
I will have to introduce an extra dao-call, AuthorDao.findByBookId(..).
This is not good in terms of both complexity and performance.

B)
Another possible solution is to introduce a layer between my DAO and
WebService implementation which nulls out potentional cyclic references,
example:

@WebService
public class LibraryService {
 private AuthorDao authorDao;
 private BookDao bookDao;
 
 public Author findAuthorById(Long id) {
  Author author = authorDao.find( id );
  for( Book book : author.getBooks() ) {
   book.setAuthor( null );
  }
  return author;
 }
 
 public Book findBookById( Long id ) {
  Book book = bookDao.find( id );
  book.getAuthor().setBooks( null );
  return book ;
 }
}

Pros:
* I can keep my Domain model and traverse both ways (serverside)
* Model can be traversed all ways, but only one way at a time (if that
makes sense). See cons for details about this 


Cons:
* Inconsitency in model which cannot be traversed both ways on client
side.
Books belong to an Author yet Book.author is null. 
** When retrieveing a book, you can traverse the book and find its
author, but not go back and find the author's books. 
** When retrieving an author, you can traverse the author and find her
books, but not go back and find the books' author. 
* SOAP is only ONE of the remoting protocols used for exposing my web
services - Spring HTTP Invoker, which is capable of handling cyclic
references, is another one. I will have to maintain two service
implementations, one for CXF/SOAP where Book.author is nulled ouit, and
one for SPRING where Book.author is kept.

C)
Create a WS-specific domain model and introduce mapping between this
model and my "proper" domain model:

public class AuthorWS{
   private Long id;
   private String name;
   private Collection<Long> bookIds;
}

public class BookWS{
   private Long id;
   private String title;
   private Long authorId;
}


Pros:
* I can keep my domain model and traverse the model at server-side. 
* Data returned from my Web Service will be consistent: no references
are nulled out. 

Cons:
* Data returned from Web Service is "flat" instead of "good" OO-model:
When I have found a Book, i have to make another call to get the Author
(and vice versa). This will affect both client-code design and
performance.
* Complexity at serverside: introduces extra layer which handles
mapping.

D)
What have I missed? What is your best practice?
--
View this message in context:
http://www.nabble.com/OO-%3C-%3E-WS-paradigm-mismatch-tp22286364p2228636
4.html
Sent from the cxf-user mailing list archive at Nabble.com.