The Scene Graph and Object3D

Meshes, lights, cameras and the Scene extend Object3D: a local position, rotation (radians), scale, parent, children, name and free userData, composed into matrixWorld (Scene Graphs's scene graph). A Group draws nothing and moves its children. In bookshelf.js, createBook(book) returns a 0.42 x 0.98 x 0.12 m box named after the title, and createShelf() a group of a plank and six books:

Local and world positions in the shelf group, run in NodeJavaScript
import * as THREE from 'three';
import { createShelf } from './bookshelf.js';
const scene = new THREE.Scene();
const shelf = createShelf();                      // a Group: a plank and six book meshes
shelf.position.set(0, 1.2, -2);
shelf.rotation.y = Math.PI / 2;                   // a quarter turn about y
scene.add(shelf);
const harbor = scene.getObjectByName('The Quiet Harbor');
const world = harbor.getWorldPosition(new THREE.Vector3());
console.log(harbor.position.x.toFixed(3), world.z.toFixed(3));
const lamp = new THREE.PointLight('#fff3e0', 5);
scene.add(lamp).position.set(0, 3, 0);            // the trap: add() returns the scene
console.log(scene.position.y, lamp.position.y);
Output
-1.296 -0.704
3 0

The group's turn sent the book's local x offset to world z (-2 + 1.296). The last line is a classic silent bug.

A shelf Group turned by a slider: a child's local position stays, its world position movesHTMLLive
<!doctype html>
<style>
  body { margin: 0; background: #f5f1e8; font: 13px system-ui, sans-serif; color: #263238; }
  canvas { display: block; max-width: 100%; }
  .ui { padding: 6px 8px; }
  code { background: #fff; padding: 1px 4px; }
</style>
<script type="importmap">
{ "imports": {
  "three": "https://cdn.jsdelivr.net/npm/three@0.186.1/build/three.module.js",
  "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.186.1/examples/jsm/" } }
</script>
<div class="ui">shelf.rotation.y <input type="range" id="turn" min="0" max="180" value="90"> <span id="deg"></span></div>
<div class="ui" id="out"></div>
<script type="module">
import * as THREE from 'three';

const BOOKS = [['The Quiet Harbor', '#1f5f8b'], ['Patterns of the Deep Web', '#5b3f99'],
  ['Salt and Saffron', '#e09a10'], ['Small Steps to Big Summits', '#3f7d3a'],
  ["The Clockmaker's Paradox", '#b5452f'], ['Gardens in Glass', '#2a9d8f']];

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
renderer.setSize(600, 300);
document.body.prepend(renderer.domElement);
const scene = new THREE.Scene();
scene.background = new THREE.Color('#f5f1e8');
const camera = new THREE.PerspectiveCamera(40, 2, 0.1, 50);
camera.position.set(2.8, 2.3, 1.4);
camera.lookAt(0, 1.1, -1.9);
scene.add(new THREE.HemisphereLight('#ffffff', '#8d6e63', 1.2));
const sun = new THREE.DirectionalLight('#ffffff', 2.5);
sun.position.set(2, 5, 3);
scene.add(sun, new THREE.GridHelper(8, 16, '#b0a898', '#d8d0c0'), new THREE.AxesHelper(1));

// createShelf(): a Group holding a plank and six named books
function createShelf() {
  const shelf = new THREE.Group();
  const plank = new THREE.Mesh(new THREE.BoxGeometry(3, 0.06, 0.4),
    new THREE.MeshStandardMaterial({ color: '#8d6e63' }));
  plank.position.y = -0.52;
  shelf.add(plank);
  BOOKS.forEach(([title, color], i) => {
    const book = new THREE.Mesh(new THREE.BoxGeometry(0.42, 0.98, 0.12),
      new THREE.MeshStandardMaterial({ color }));
    book.name = title;                               // findable with getObjectByName
    book.position.x = (i - 2.5) * 0.5184;            // local offset inside the group
    shelf.add(book);
  });
  return shelf;
}

const shelf = createShelf();
shelf.position.set(0, 1.2, -2);
scene.add(shelf);
shelf.add(new THREE.AxesHelper(0.6));                 // the group's local axes

const harbor = scene.getObjectByName('The Quiet Harbor');
harbor.material.emissive.set('#333');
const marker = new THREE.Mesh(new THREE.SphereGeometry(0.06), new THREE.MeshBasicMaterial({ color: '#e09a10' }));
scene.add(marker);

const out = document.getElementById('out');
const fmt = (v) => v.toArray().map((n) => n.toFixed(2)).join(', ');
function update(degrees) {
  shelf.rotation.y = THREE.MathUtils.degToRad(degrees);
  shelf.updateMatrixWorld();
  const world = harbor.getWorldPosition(new THREE.Vector3());
  marker.position.copy(world).y += 0.6;
  document.getElementById('deg').textContent = degrees + '°';
  out.innerHTML = `The Quiet Harbor: local <code>(${fmt(harbor.position)})</code>, ` +
    `world <code>(${fmt(world)})</code>`;
  renderer.render(scene, camera);
}
const slider = document.getElementById('turn');
slider.addEventListener('input', () => update(+slider.value));
update(90);
</script>