2
\$\begingroup\$

I am developing a server for a web-client. Basically it's just a simple CMS to modify some data. My problem is that I never had a project like this where I have to take care about the server and the client and of course everything that happens between those two.

I've started to implement a database with Hibernate which is a great tool but it has its learning curve - I can tell. However, the client is also written in Java. Here I am using GWT. The thing now is that my database keeps growing and is getting more and more complex and I am not quite sure if the way I am doing it is sane.

To keep it simple assume we just have e.g. a Store that has collections of Item, those again have an ItemDescription and those descriptions e.g. do have foreign keys to Language in order to provide different translations. So far so good.

What am I sending to the client? What I did for now is I built POJO objects that basically are mirror images of the Hibernate entities:

public class LanguageDTO implements Serializable {
    private static final long serialVersionUID = 1991918538222888435L;

    private Long id;
    private String name;

    // Getter/setter
}

and with Hibernate:

@Entity
public class Language implements Serializable {
    private static final long serialVersionUID = 3968717758435500381L;

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="ID")
    private Long id;

    @NotNull
    @Column(name="LANGUAGE_CODE", length=2,unique=true)
    private String languageCode;

    @NotNull
    @Column(name="LANGUAGE_NAME", length=50,unique=true)
    private String languageName;

    public Language() { }

    // Getter/setter
}

I have done the same to Store, Item, ItemDescription etc. and I am sending the actual IDs from the database to the client and back to the server for persistence routines.

But I didn't stop there. I have also "mirrored" the relations between the POJO objects. That means I have basically almost an exact copy of the database on the client side (of course only the data that corresponds to a logged in user).

Is it a good idea to do that?

I personally like this because to me nothing really changes. Since I know the database I have no troubles finding the stuff I need on the client. Another thing that I like is that I e.g. have a map Map<Long, ItemDescription> on the client side that mirrors the relation Language.id and ItemDescription. This is convenient since The client can work with different languages right away.

But here comes the thing that is starting to make me a little wonder if I should do it like that. On the server I have to util classes ConvertToDTO and ConvertToEntity which have a lot of functions like that:

public static Item convertItem(Store store, ItemDTO itemDto) {

    Item item = null;

    Session session = HibernateSession.openSession();
    session.beginTransaction();

    if(itemDto != null) {
        if(itemDto.getItemId() != null) {
            item = (Item)session.get(Item.class, itemDto.getItemId());
            if( item != null ) {
                session.close();
                return new Item(store, itemDto.getAvailable());
            }
        }
    }

    item = new Item(store, false, false);

    session.save(item);
    session.getTransaction().commit();
    session.close();

    return item;
}

which basically converts a DTO POJO into a Entity class. These static functions are using each other:

public static Tool convertTool(Store store, Language language, ToolDTO toolDto) {

    ItemDTO itemDto = toolDto.getItem();
    Item item = ConvertToEntity.convertItem(store, itemDto);
    Tool tool = new Tool(store, item);

    toolDto.getItemDescription(language.getId());

    // ...

    return tool;
}

which is the downside of the way I decided to do it. I have to have the complete entity data to restore an entity object and e.g. if the user created a new Item I have to check this:

if(itemDto != null) {
    if(itemDto.getItemId() != null) {
        item = (Item)session.get(Item.class, itemDto.getItemId());
        if( item != null ) {
            session.close();
            return new Item(store, itemDto.getAvailable());
        }
    }
}

item = new Item(store, false);

session.save(item);
session.getTransaction().commit();
session.close();

and I am not sure if I should do that either like this.

I have the feeling that I'm doing a few things much more complicated than I should. Another question that arises is "how much" data should I send to the client and how to keep track on changes the user is doing to the DTO objects. After all I have to persist the returning DTO objects and in order to do that I have to check and validate everything field by field.

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

In complex projects, it is always a good idea to use domain objects and DTOs. If you use your entities in other layers of your applications, you may sooner-or-later notice problems. And if you are already using them in your whole app, it is difficult to change is afterwards.

  • other layers, which are not related to persistence, need to have a dependency on Hibernate
  • if you use any other persistence technology, like NoSQL DB, you have to convert them anyway using some Translator. Hibernate and relational databases are just one possibility.
  • contraints like NotNull may be OK for your DB, but are you sure it cannot be null in the UI?
  • in other layers, you probably will not need all fields, so you do not need to transfer them to them.

So IMO it is a good idea to translate your objects between the layers. It can be tedious to build parallel objects for each layer, I think it is worth it. Dozer (not intended as spam) is a framework which may help to reduce that (although I have not used it yet).

Converters should just convert, not starting transactions (like in convertItem()). You could structure your application using DAOs, repositories and services. See DAO, Repositories and Services in DDD. Code like (Item)session.get(Item.class, itemDto.getItemId()) would probably be in a DAO, in an getById(long id)-method. Transactions are usually started on the service layer, using declarative transactions like in Java EE or Spring, you don't have to handle transactions manually.

A Service could have a simple method addItemToStore(). You could also introduce still a layer between the service and the DAO, but it is a good start for a simple service.

@Transactional
public void addItemToStore(Store store, Item item) {

    StoreEntity storeEntity = storeDao.getById(store.getId());
    ItemEntity itemEntity = itemDao.getById(item.getId());

    storeEntity.getItems().add(itemEntity);
    storeDao.save(storeEntity);
}
\$\endgroup\$
6
  • \$\begingroup\$ Hi! Well, convertItem() in my case is basically the ugly way to implement a DAO object I guess. You're saying that a convert should not start transactions and I totally agree on that - but I have to e.g. add a new Item first and then ItemDescription to the database if the user creates a new one because in that case getId() from Item would return null. Would the DAO object handle that? \$\endgroup\$ Jul 7, 2015 at 9:58
  • \$\begingroup\$ What would storeDao return if e.g. store.getId() was null like in storeDao.getById(store.getId()); Would it create a new entry if store != null? \$\endgroup\$ Jul 7, 2015 at 10:04
  • \$\begingroup\$ A DAO Object only handles things like save, get, maybe find, but it would not be responsible for creating a new entry. The method addItemToStore assumes that the Store and the Item are already existing, it only adds the "link" between those entities. You could add other methods for creating Stores and Items or a method createItemAndAddToStore(), depending on your requirements. \$\endgroup\$
    – user140547
    Jul 7, 2015 at 10:15
  • \$\begingroup\$ I'm refactoring right now but I don't get my head around this. I would have to re-implement a lot of functionality like Hibernate save(), saveOrUpdate(), etc. I am also not sure how this would work for more complex stuff. Would a DAO be able to work with other DAOs and check e.g. if Item and ItemDescription do exist and if not create them? The little example you showed does not cover all the checking that must go on before calling save(). \$\endgroup\$ Jul 7, 2015 at 13:18
  • \$\begingroup\$ If you have complex logic to do before saving, this should not happen in a DAO. You can do that in a Repository, which builds the entity with its collections from the object which it gets from the other layers and then save the entity. \$\endgroup\$
    – user140547
    Jul 7, 2015 at 13:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.