Search code examples
javamodel-view-controller

Not exposing IDs in a web application in case of data manipulation


Let's consider an example of a repository of books, stored in an entity "Book" in a classic MVC application. Books have non-unique names, which allows for duplicates, therefore lookup by the book name does not always provide unique selection result.

Considering an entity of Book like this:

@Entity
public class Book {
@Id @GeneratedValue
private Long id;

private String title;
private String ISBN;

@ManyToMany // ommiting mapping details
private Set<Author> bookAuthors;

// getters, setters, etc.
}

we may have a DTOs for specific purposes, like showing data on webpage

public class BookDto {
private String title;
private String ISBN;
private Set<Author> bookAuthors;
// getters, setters, etc.
}

now correct me if I'm wrong, but the good practice is to hide IDs, meaning, in this case, to not include ID field in DTO? If yes, then let's assume we have "list all books" page, on which we display details about book, and offer a "Edit book data" button (or delete, or whatever form of manipulating data of a single entry), which should invoke a coded functionality for the specific book. For me it would look like this (without hiding ID, as this is the only way to correctly identify non-unique titled book):

<button type="button" onclick="editBookDetails(id)">Edit book details</button>

but as mentioned before - we should hide IDs as deep as possible and avoid exposing it to the surface layers. But how should such functionality be written then? How to identify entry to edit, if not by its unique ID?

Assuming we have hidden the IDs, what is the best practice for constructing such operations? Maybe there are some approaches I am not aware of.

P.S. Yes, I know, ISBN is kinda unique. But there are multiple other examples of data that are not books, and do not have such "public" unique ID, safe to expose, like books have.


Solution

  • Do not eliminate IDs

    the good practice is to hide IDs

    As discussed in the Comments, no, your premise is incorrect. There is nothing wrong with including ID values as part of your entities.

    Indeed, you must bring the ID values along if you intend further work. For example, when presenting a list of objects for the user to edit or delete, you will need the ID to distinguish which object is which. The ID after all is the identifier, identifying which object needs to be edited or deleted.

    Perhaps suppress display

    The user may not be particularly interested in the ID value. If so, hide the value from display in the user-interface. Behind the scenes your app still needs to identify one object from another.

    For example, when listing objects in a tabular grid, with a column for every field in the class, you may choose to suppress the display of the ID column. But you still need that ID value for the editing/deleting as described above.

    Sensitive identifiers

    As commented by Elliott Frisch, you may have identifiers that represent sensitive information. For example government-issued identifiers such as Social Security Number (SSN) in the US.

    Generally, sensitive values should not be used liberally across your app and systems. Instead of using a sensitive value as the identifier (a natural key), generate another identifier in its place (a surrogate key). This is why an employee is assigned an Employee ID upon hiring, to keep their private data locked away while providing another identifier to be used more liberally.

    ISBN

    ISBN is kinda unique

    No, incorrect. An ISBN number is very much unique — that is its purpose, to uniquely identify every published book.

    For tracking existing books, you would certainly be wise to use ISBN as the natural key of a Book entity.

    The ISBN would of course not be appropriate in software to assist a publisher. A book does not get an ISBN assigned until it is formally published. So during the proposal, drafting, editing, and production phases, publishing software would need to assign a surrogate key as the identifier.